python drf各類元件的用法和作用
阿新 • • 發佈:2021-01-13
DRF元件的用法和作用
認證
自定義認證的類 """ from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from authapp.models import UserToken class MyOrderAuthentication(BaseAuthentication): 在這裡實現認證的邏輯 def authenticate(self,request): token = request._request.GET.get('token') # 獲取到token之後,需要在資料庫中查詢token obj = UserToken.objects.filter(token=token).first() if not obj: # 沒有通過認證 raise AuthenticationFailed('認證失敗') # 返回元組( user,auth ) return (obj.user,obj) """ 使用區域性配置(在檢視函式中) """ class OrderView(APIView): # 通過authentication_classes設定認證類 authentication_classes = [MyOrderAuthentication,] # 通過authentication_classes設定為空列表,就不再進行認證了 # authentication_classes = [] """ 全域性配置 """ REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES':['unitls.authentication.MyOrderAuthentication'],} """ 設定匿名使用者 """ REST_FRAMEWORK = { 'UNAUTHENTICATED_USER': lambda :"匿名使用者",'UNAUTHENTICATED_TOKEN': lambda :'123456',} """ ## 最最重要的,理清認證的原始碼
許可權
自定義許可權類 """ from rest_framework.permissions import BasePermission class MyOrderPermission(BasePermission): #自定義許可權認證的類,必須要實現has_permission方法 message = '你不是超級使用者,沒有許可權訪問' def has_permission(self,request,view): #Return `True` if permission is granted,`False` otherwise. #返回True表示有許可權訪問,返回False表示沒有許可權訪問 if request.user.user_type != 3: return False return True """ 區域性使用 """ class OrderView(APIView): # permission_classes設定許可權類 permission_classes = [MyOrderPermission,就不再進行許可權認證了 permission_classes = [] """ 全域性的設定 """ REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES':['unitls.permission.MyOrderPermission'],} """ 最最重要的,理清許可權認證的原始碼
分頁
自定義分頁類PageNumberPagination # 自定製分頁類 class MyPageNumberPagination(PageNumberPagination): """http://127.0.0.1:8000/api/userpage/?page=1&pagesize=10""" # page_size每一返回多少條 page_size = 5 # 設定分頁的引數名 page_query_param = 'page' # 設定每頁返回資料量的引數名 page_size_query_param = 'pagesize' # 設定每頁最大返回的條數 max_page_size = 6 使用 class UsersPageView(APIView): def get(self,*args,**kwargs): # 獲取表中所有使用者的row(記錄) obj = models.UserInfo.objects.all() #例項化分頁的類 #page_obj = PageNumberPagination() page_obj = MyPageNumberPagination() #獲取分頁資料 page_data = page_obj.paginate_queryset( queryset=obj,request=request,view=self) # 序列化 ser = UsersSerializer(instance=page_data,many=True) # return Response(ser.data) #get_paginated_response會返回上一頁下一頁和總條數 return page_obj.get_paginated_response(ser.data) 自定義分頁類LimitOffsetPagination from rest_framework.pagination import LimitOffsetPagination class MyLimitOffsetPagination(LimitOffsetPagination): """http://127.0.0.1:8000/api/userpage/?limit=10&offset=0""" default_limit = 5 limit_query_param = 'limit' offset_query_param = 'offset' max_limit = 7 自定義分頁類CursorPagination(會對分頁引數進行加密) from rest_framework.pagination import CursorPagination class MyCursorPagination(CursorPagination): """http://127.0.0.1:8000/api/userpage/?cursor=cD01""" cursor_query_param = 'cursor' page_size = 4 #返回資料市的排序的方式 ordering = '-id' max_page_size = 8 設定全域性的分頁 """ REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS':'unitl.pagination.MyCursorPagination','PAGE_SIZE':3 } """
檢視
以前 (Django的View) """ class MyView(View) ..... """ 現在(rest_framework的APIView) """ class MyView(APIView) ..... """ 其他檢視的使用 第一個:GenericAPIView 檢視的使用 (跟繼承自APIViewq其實一樣,只是我們在外面邏輯, GenericAPIView在內部c定製方法幫我們實現了) """ from rest_framework.generics import GenericAPIView class BookinfoSeralizer(serializers.ModelSerializer): class Meta: model = models.BookInfo fields = "__all__" class BookView(GenericAPIView): # queryset: 設定獲取的資料 queryset = models.BookInfo.objects.all() # serializer_class: 設定序列化的類 serializer_class = BookinfoSeralizer # pagination_class : 設定分頁的類 pagination_class = MyPageNumberPagination def get(self,**kwargs): obj = self.get_queryset() #=> obj = models.BookInfo.objects.all() # 獲取當前分頁的資料 page_data = self.paginate_queryset(obj) #=>page_obj = MyPageNumberPagination() #獲取分頁資料page_data = page_obj.paginate_queryset() # 獲取序列化之後的資料 ser = self.get_serializer(instance=page_data,many=True) #->ser = BookinfoSeralizer(instance=page_data,many=True) return Response(ser.data) """ 第二個:GenericViewSet 檢視的如下使用,注意路由會發生變化 """ class BookView(GenericViewSet): # queryset: 設定獲取的資料 queryset = models.BookInfo.objects.all() # serializer_class: 設定序列化的類 serializer_class = BookinfoSeralizer # pagination_class : 設定分頁的類 pagination_class = MyPageNumberPagination def list(self,**kwargs): obj = self.get_queryset() #=> obj = models.BookInfo.objects.all() # 獲取當前分頁的資料 page_data = self.paginate_queryset(obj) #=>page_obj = MyPageNumberPagination() #獲取分頁資料page_data = page_obj.paginate_queryset( # 獲取序列化之後的資料 ser = self.get_serializer(instance=page_data,many=True) return Response(ser.data) """ 路由會發生變化,配置如下 """ url(r"bookpage/$",views.BookView.as_view({'get': 'list'}),name='bookpage') """ 第三個:ListModelMixin,CreateModelMixin,RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin 等檢視的使用 """ from rest_framework.mixins import ListModelMixin,UpdateModelMixin from rest_framework.viewsets import GenericViewSet # ListModelMixin : 返回列表資料據( get請求) # CreateModelMixin : 新增一條資料 (Post請求) # RetrieveModelMixin,: 獲取詳情資料 (get請求) # DestroyModelMixin,: 刪除資料的時候 (delete) # UpdateModelMixin : 跟新資料的時候使用 (put) class BookView(ListModelMixin,UpdateModelMixin,GenericViewSet): # queryset: 設定獲取的資料 queryset = models.BookInfo.objects.all() # serializer_class: 設定序列化的類 serializer_class = BookinfoSeralizer # pagination_class : 設定分頁的類 pagination_class = MyPageNumberPagination """ 第四個:ModelViewSet檢視的使用 ModelViewSet繼承自istModelMixin,UpdateModelMixin檢視 如果要實現最基本的增刪改查功能,就直接繼承自ModelViewSet """ from rest_framework.viewsets import ModelViewSet class BookView(ModelViewSet): # queryset: 設定獲取的資料 queryset = models.BookInfo.objects.all() # serializer_class: 設定序列化的類 serializer_class = BookinfoSeralizer # pagination_class : 設定分頁的類 pagination_class = MyPageNumberPagination """ 檢視使用小總結 只想實現簡單的增刪改查 ModelViewSet 只想增 CreateModelMixin,GenericViewSet 只想增刪改 CreateModelMixin,GenericViewSet 如果檢視中的業務邏輯複雜,以上都不能滿足的時候,直接使用 APIView #自動路由配置 """ from django.conf.urls import url,include from api import views from rest_framework import routers router = routers.DefaultRouter() router.register(r"bookpage",views.BookView,base_name='bookpage') urlpatterns = [ url(r'v1/',include(router.urls)),] """ 自動路由會生成四個介面 ^api/ v1/ ^bookpage/$ [name='bookpage-list'] ^api/ v1/ ^bookpage\.(?P<format>[a-z0-9]+)/?$ [name='bookpage-list'] ^api/ v1/ ^bookpage/(?P<pk>[^/.]+)/$ [name='bookpage-detail'] ^api/ v1/ ^bookpage/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='bookpage-detail']
頻率限制節流
節流: 自定義節流類 """ VISIT_RECORD = {} class VisitThrottle(object): def __init__(self): self.history = None def allow_request(self,view): #實現節流的邏輯 #基於ip做節流 # #獲取使用者訪問的IP地址 # ip_address = request._request.META.get('REMOTE_ADDR') ctime = time.time() # if ip_address not in VISIT_RECORD: # #第一次訪問的時候將訪問的時間儲存在字典中(ip地址為Key,訪問的時間為value值) # VISIT_RECORD[ip_address] = [ctime,] # # #第二次訪問的時候取出訪問的歷史記錄 # history = VISIT_RECORD[ip_address] # 基於使用者的節流 username = request.user.username if username not in VISIT_RECORD: VISIT_RECORD[username] = [ctime,] history = VISIT_RECORD[username] self.history = history while history and history[-1] < ctime - 10: #如果訪問的時間記錄超過60秒,就把超過60秒的時間記錄移除 history.pop() if len(history) < 6: history.insert(0,ctime) return True return False def wait(self): #一旦使用者訪問次數到達閥值,顯示使用者需要等待的時間 ctime = time.time() #09:54:30 09:54:28 return 10 - (ctime - self.history[-1]) """ 區域性使用 """ class OrderView(APIView): # throttle_classes設定節流類 throttle_classes = [VisitThrottle,] """ 全域性設定 """ REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES':['unitls.throttle.VisitThrottle'],} """ 使用DRF內建的限頻類 """ from rest_framework.throttling import SimpleRateThrottle #推薦使用這種 class VisitThrottle(SimpleRateThrottle): #沒有登入使用者,每分鐘訪問10次 scope = 'logined' def get_cache_key(self,view): return request.user.username """ 全域性設定 """ REST_FRAMEWORK = { 'DEFAULT_THROTTLE_RATES':{ 'unlogin':'10/m','logined':'3/m',},'DEFAULT_THROTTLE_CLASSES':['unitls.throttle.VisitThrottle'],} """
版本控制
自定義版本控制類 """ class ParmasVersion(object): def determine_version(self,**kwargs): version = request.query_params.get('version') return version """ 使用(區域性) """ class VersionView(APIView): #設定獲取版本的類 versioning_class = ParmasVersion """ 全域性設定 """ 'DEFAULT_VERSIONING_CLASS':'unitls.version.ParmasVersion',""" 使用 DRF內建的版本控制類QueryParameterVersioning(區域性) """ from rest_framework.versioning import QueryParameterVersioning class VersionView(APIView): #設定獲取版本的類 versioning_class = QueryParameterVersioning """ 設定檔案中的配置資訊 """ REST_FRAMEWORK = { 'VERSION_PARAM':'version','DEFAULT_VERSION':'v1','ALLOWED_VERSIONS':['v1','v2'],} """ 全域性設定 """ REST_FRAMEWORK = { 'VERSION_PARAM':'version','DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.QueryParameterVersioning',} """ 使用 DRF內建的版本控制類URLPathVersioning(區域性) """ from rest_framework.versioning import URLPathVersioning class VersionView(APIView): #設定獲取版本的類 versioning_class = URLPathVersioning """ 設定檔案中的配置資訊 """ REST_FRAMEWORK = { 'VERSION_PARAM':'version',} """ 全域性設定 """ REST_FRAMEWORK = { 'VERSION_PARAM':'version','DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning',} """ 如果使用URLPathVersioning,路由格式如下 """ url(r"^(?P<version>[v1|v2]+)/version/",VersionView.as_view(),name='vvvv') """ #使用 DRF內建的版本控制類URLPathVersioning 反向生成url地址 """ #反向生成url地址 reverse obj = request.versioning_scheme url1 = obj.reverse(viewname='orders',request=request) #使用django的reverse方法反響生成url地址 from django.urls import reverse url2 = reverse(viewname='orders',kwargs={'version':'v2'}) """
解析器
因為開發人員post請求上傳資料時,傳遞的資料型別不同,我們可能在request._request.POST中獲取不到資料 case1: Content-Type : application/x-www-form-urlencoded 服務端接收到的post請求的資料格式:username=xxxxx&age=18&sex=男 我們就可以在request._request.POST中獲取到資料 """ class UserInfoView(APIView): def post(self,**kwargs): username = request._request.POST.get('username') age = request._request.POST.get('age') sex = request._request.POST.get('sex') """ case2:Content-Type:application/json 服務端接收到的post請求的資料格式就是json資料:{"username":"xxxx","age":"18","sex":"男"} 在request._request.POST中就獲取不到資料,但是在request.body中可以拿到 """ class UserInfoView(APIView): def post(self,**kwargs): import json data = json.loads(request.body.decode('utf8')) print(data) """ DRF內建的解析器FormParser,JSONParser 使用(區域性): """ from rest_framework.parsers import FormParser,JSONParser class UserInfoView(APIView): parser_classes = [FormParser,JSONParser] #這時DRF 內部程式碼會根據request.Content-Type和解析器支援的media_type比較 從而選擇對應的解析器 def post(self,**kwargs): # 如果使用JSONParser、FormParser解析資料的話 data = request.data print(data) """
渲染器
渲染器 INSTALLED_APPS = [ 'rest_framework',] from rest_framework.renderers import BrowsableAPIRenderer,JSONRenderer,AdminRenderer class BookView(ModelViewSet): # 設定渲染器型別 renderer_classes = [JSONRenderer]
序列化
Django的序列化 """ #django序例化方式一 books = models.BookInfo.objects.all().values('id','bookname') books = list(books) print(type(books),books) self.ret['books'] = books #django序例化方式二 books = models.BookInfo.objects.all() books = [model_to_dict(item) for item in books] self.ret['books'] = books """ DRF 序列化 第一種:繼承自serializers.Serializer """ class BookDetailSerializer(serializers.Serializer): # 正常的欄位序列化 id = serializers.IntegerField() bookname = serializers.CharField() author = serializers.CharField() category = serializers.IntegerField() bookdesc = serializers.CharField() # 獲取列舉型別的文字是 source=get_欄位名_display status = serializers.CharField( source='get_status_display' ) categoryname = serializers.CharField( source='get_category_display' ) # 自定義方法獲取欄位 chpaters = serializers.SerializerMethodField() #序列化時可以自定義方法獲取欄位 def get_chpaters(self,row): """ row - > bookinfo """ chpaters = models.ChpaterInfo.objects.filter(book=row) ser = ChpaterSerializer(instance=chpaters,many=True,context=self.context ) return ser.data """ 序列化時生成url """ url = serializers.HyperlinkedIdentityField( view_name='chpaterdetail',lookup_field='id',lookup_url_kwarg='pk',) """ 注意:如果序列化類中使用HyperlinkedIdentityField生成url,那我們在序例化時新增context={'request': request} """ ser = BookDetailSerializer( instance=obj,many=False,context={'request': request} ) """ 如果出現關聯關係時,獲取model對像的某一個欄位 """ bookname = serializers.CharField(source='book.bookname') """ 第二種繼承自:serializers.ModelSerializer """ class ChpaterDetailSerializer(serializers.ModelSerializer): #使用ModelSerializer進行章節詳情的序列化 bookname = serializers.CharField(source='book.bookname') class Meta: model = models.ChpaterInfo #fields = "__all__" fields = ['id','bookname'] """ DRF (序列化時)自定義方法獲取資料 """ book = serializers.SerializerMethodField() """ """ def get_book(self,row): """ row - > UserInfo""" print('======',row.book.all()) ser = UsersBooksSerializer( instance=row.book.all(),many=True ) return ser.data """ DRF depth深度的使用 # depth會根據關聯的資料不停的深入將資料獲取出來(最多不超過10層) # depth = 1 """ class UsersSerializer(serializers.ModelSerializer): class Meta: model = models.UserInfo fields = "__all__" #depth會根據關聯的資料不停的深入將資料獲取出來(最多不超過10層) depth = 1 """ DRF序列化的驗證功能 """ class UsersSerializer(serializers.ModelSerializer): #自定義驗證錯誤的資訊 username = serializers.CharField(error_messages={'required':'使用者名稱不能為空'}) class Meta: model = models.UserInfo fields = "__all__" """ """ class UsersView(APIView): def post(self,**kwargs): """DRF 序列化自帶驗證功能""" data = request.data #print(data) ser = UsersSerializer(data=data) if ser.is_valid(): # ser.is_valid()y驗證資料的有效性 print('驗證後的資料',ser.validated_data) #驗證後的資料正確後,儲存資料至資料庫 ser.save() else: #上傳資料不符合規範時ser.errors,返回錯誤詳細 print(ser.errors) return Response(data) """ 自定義欄位驗證規則 """ class UsersInfoSerializer(serializers.ModelSerializer): username = serializers.CharField(error_messages={'required':'使用者名稱不能為空'}) class Meta: model = models.UserInfo fields = "__all__" # 使用者名稱中必須包含老王兩個字,不包含則認為名字無效 def validate_username(self,validated_value): print(validated_value) from rest_framework.exceptions import ValidationError if '老王' not in validated_value: #驗證不通過,丟擲異常 raise ValidationError('使用者名稱不合法') #驗證通過,返回資料 return validated_value """
以上就是python drf各類元件的用法和作用的詳細內容,更多關於python drf元件的資料請關注我們其它相關文章!