1. 程式人生 > 實用技巧 >drf認證、許可權、頻率、過濾、排序、異常處理

drf認證、許可權、頻率、過濾、排序、異常處理

認證元件

使用方法:

1、新建一個認證類檔案,繼承BaseAuthentication

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.response import Response
from app03 import models
class Bookauth(BaseAuthentication):

    def authenticate(self, request):
        token
=request.META.get('HTTP_TOKEN') if token: token_obj=models.UserToken.objects.filter(token=token).first() if token_obj: return token_obj.user,token else: raise AuthenticationFailed('驗證失敗') else: raise AuthenticationFailed(
'請求頭沒有token資訊')

2、認證類區域性配置

'''
區域性使用,在列表內發那個值認證類
區域性禁用,設定列表為空
'''
class Login(APIView):
    #區域性使用
    authentication_classes = [Bookauth,]
    #區域性禁用
    # authentication_classes = []
    def post(self, request):
        user = models.User.objects.filter(username=request.data.get('username'), password=request.data.get('
password')).first() if user: token = uuid.uuid4() models.UserToken.objects.update_or_create(user=user, defaults={'token': token}) return Response("{'msg':'登陸成功'}") else: return Response("{'msg':'使用者不存在'}")

3、全域性配置,在settings檔案中配置:

REST_FRAMEWORK={
    # "DEFAULT_AUTHENTICATION_CLASSES":["app04.MyAuth.Login_auth",]
    "DEFAULT_AUTHENTICATION_CLASSES":["app03.BookAuth.Bookauth",]
}

認證原始碼分析:

#1 APIVIew----》dispatch方法---》self.initial(request, *args, **kwargs)---->有認證,許可權,頻率
#2 只讀認證原始碼: self.perform_authentication(request)
#3 self.perform_authentication(request)就一句話:request.user,需要去drf的Request物件中找user屬性(方法) 
#4 Request類中的user方法,剛開始來,沒有_user,走 self._authenticate()

#5 核心,就是Request類的 _authenticate(self):
    def _authenticate(self):
        # 遍歷拿到一個個認證器,進行認證
        # self.authenticators配置的一堆認證類產生的認證類物件組成的 list
        #self.authenticators 你在檢視類中配置的一個個的認證類:authentication_classes=[認證類1,認證類2],物件的列表
        for authenticator in self.authenticators:
            try:
                # 認證器(物件)呼叫認證方法authenticate(認證類物件self, request請求物件)
                # 返回值:登陸的使用者與認證的資訊組成的 tuple
                # 該方法被try包裹,代表該方法會拋異常,拋異常就代表認證失敗
                user_auth_tuple = authenticator.authenticate(self) #注意這self是request物件
            except exceptions.APIException:
                self._not_authenticated()
                raise

            # 返回值的處理
            if user_auth_tuple is not None:
                self._authenticator = authenticator
                # 如何有返回值,就將 登陸使用者 與 登陸認證 分別儲存到 request.user、request.auth
                self.user, self.auth = user_auth_tuple
                return
        # 如果返回值user_auth_tuple為空,代表認證通過,但是沒有 登陸使用者 與 登陸認證資訊,代表遊客
        self._not_authenticated()

許可權

# APIView---->dispatch---->initial--->self.check_permissions(request)(APIView的物件方法)
    def check_permissions(self, request):
        # 遍歷許可權物件列表得到一個個許可權物件(許可權器),進行許可權認證
        for permission in self.get_permissions():
            # 許可權類一定有一個has_permission許可權方法,用來做許可權認證的
            # 引數:許可權物件self、請求物件request、檢視類物件
            # 返回值:有許可權返回True,無許可權返回False
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )
許可權原始碼分析

使用方法:

(1)自定義一個許可權類,繼承BasePermission類,程式碼如下:

from rest_framework.permissions import BasePermission

class User_per(BasePermission):
    def has_permission(self, request, view):
        user=request.user
        is_staff=user.is_staff
        if is_staff==1: #1:管理員 2:普通使用者 3:匿名使用者
            return True
        else:
            return False

(2)全域性配置跟區域性配置:

REST_FRAMEWORK = {
    # 許可權全域性配置
    "DEFAULT_PERMISSION_CLASSES": ['app01.app_per.User_per', ]
}

#區域性配置
class TestView1(APIView):
    #區域性禁用
    permission_classes = []
    #區域性配置
    permission_classes = [User_per]
    def get(self,request):
        return Response('我是匿名使用者')

內建許可權:

from rest_framework.permissions import IsAdminUser
from rest_framework.authentication import SessionAuthentication
class Supper(APIView):
    permission_classes = [IsAdminUser]
    authentication_classes = [SessionAuthentication]
    def get(self,request):
       
        return Response('這是超級管理員的測試')

頻率

全域性配置:settings檔案下

REST_FRAMEWORK = {
    #限制
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle',#未登入使用者
        'rest_framework.throttling.UserRateThrottle',#登陸使用者
    ),
    'DEFAULT_THROTTLE_RATES': {
        'anon': '3/m', #1分鐘訪問3次,m後面無論帶什麼都沒影響,因為它只擷取m
        'user': '5/m'
    },
}

區域性配置:

from rest_framework.throttling import UserRateThrottle
class TestView1(APIView):
    #頻率區域性配置
    throttle_classes = [UserRateThrottle]
    def get(self,request):
        return Response('我是匿名使用者')

過濾

使用方法:

1、安裝:pipinstall django-filter

2、在settings中進行app註冊:

INSTALLED_APPS = [
    'django.contrib.admin',
    ....'django_filters',
]

3、全域性配置:

REST_FRAMEWORK = {
   #過濾全域性配置
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
}

4、區域性配置以及在檢視類的使用

from django_filters.rest_framework import DjangoFilterBackend
class BookView(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = Book_ser
    #區域性配置
    filter_backends = [DjangoFilterBackend]
    #配置要過濾的欄位
    filter_fields = ('name','price',)

排序

1、匯入OrderingFilter模組

from rest_framework.filters import OrderingFilter

2、在檢視類上的使用:

class BookView(ModelViewSet):
    authentication_classes = []
    permission_classes = []
    throttle_classes = []

    queryset = models.Book.objects.all()
    serializer_class = Book_ser
    #區域性配置
    # filter_backends = [DjangoFilterBackend]
    # #配置要過濾的欄位
    # filter_fields = ('name','price',)
    filter_backends = [OrderingFilter]
    ordering_fields = ('id', 'price')

    '''
    使用
          -號:表示降序
    '''
    http://127.0.0.1:8000/books2/?ordering=-price
    http://127.0.0.1:8000/books2/?ordering=price

異常處理

1、自定義一個異常處理方法,用來重寫rest_framework.views中exception_handler方法:

from rest_framework.views import exception_handler
from rest_framework.response import Response
from rest_framework import status
def my_exception_handler(exc, context):
    response=exception_handler(exc, context)
    # 返回兩種情況,一個是None,drf沒有處理
    #一個是response物件,django處理了,處理格式不是很好
    
    if not response:
        if isinstance(exc, ZeroDivisionError):
            return Response(data={'status': 9999, 'msg': "除以0的錯誤" + str(exc)}, status=status.HTTP_400_BAD_REQUEST)
        return Response(data={'status':1111,'msg':str(exc)},status=status.HTTP_400_BAD_REQUEST)
    else:
        return Response(data={'status':2222,'msg':response.data.get('detail')},status=status.HTTP_400_BAD_REQUEST)

全域性配置:

REST_FRAMEWORK = {
  #異常
    'EXCEPTION_HANDLER': 'app01.wsigi.APIhandle_exceptn'#自己寫的異常處理方法
}