day74:drf:drf其他功能:認證/許可權/限流/過濾/排序/分頁/異常處理&自動生成介面文件

Poke發表於2020-10-23

目錄

1.django-admin

2.認證:Authentication

3.許可權:Permissions

4.限流:Throttling

5.過濾:Filtering

6.排序:OrderingFilter

7.分頁:Pagination

8.異常處理:Exception

9.自動生成介面文件

1.django-admin

1.先建立一個子應用,用來測試接下來drf其他的相關功能。

python manage.py startapp drf_others

2.因為接下來的功能中需要使用到登陸功能,所以我們使用django內建admin站點並建立一個管理員.

python manage.py createsuperuser
# 填一下使用者名稱、郵箱和密碼
root
1234567@qq.com
123

 

3.建立管理員以後,訪問admin站點,先修改站點的語言配置

4.訪問admin站點效果

admin後臺管理簡單使用

在應用的admin.py檔案中

from app01 import models

class StudentAdmin(admin.ModelAdmin):
    list_display = ['id','name','age','class_null']
    list_editable = ['name','age']

admin.site.register(models.Student,StudentAdmin)

建立超級管理員使用者

python manage.py createsuperuser
# 輸入使用者名稱和密碼,郵箱可以不輸入

url

path('admin/', admin.site.urls),

2.認證:Authentication

可以在配置檔案中配置全域性預設的認證方案

from rest_framework import settings

'''在settings配置檔案中,我們可以進行下面的配置來覆蓋預設配置'''
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
      # 哪個寫在前面,優先使用哪個認證
        
        'rest_framework.authentication.SessionAuthentication',  # session認證,admin後臺其實就使用的session認證,其實介面開發很少用到session認證,所以我們通過配置可以改為其他認證,比如後面專案裡面我們用到jwt,JSON WEB TOKEN認證,或者一些配合redis的認證
        
        'rest_framework.authentication.BasicAuthentication',   # 基本認證,工作當中可能一些測試人員會參與的話,他們會將一些認證資料儲存在記憶體當中,然後驗證的,我們基本上用不上
    )
}

也可以在每個檢視中通過設定authentication_classes屬性來設定,比如說我們很多介面的資料都是可以讓別人獲取資料的,

但是有可能有些介面是呼叫給別人網站的,有可能到時候我們就需要一些單獨的認證了

from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIView

class ExampleView(APIView):
    # 類屬性
    authentication_classes = [SessionAuthentication, BasicAuthentication] # 也可以寫成元組形式的,到時候我們使用我們自己開發的認證元件的時候,就需要自己寫一個認證元件類,然後寫在列表中來使用
    ...

認證失敗會有兩種可能的返回值:

  • 401 Unauthorized 未認證

  • 403 Permission Denied 許可權被禁止

示例1:自定義認證元件

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
class APIAuth(BaseAuthentication):

    def authenticate(self, request):
        print(request) 

        if 1:
            return 'xx','oo'  # request.user = 'xx'  request.auth = 'oo'

        else:
            raise AuthenticationFailed('認證失敗')

全域性使用,settings配置檔案中使用

REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
...
'four.utils.auth.APIAuth',  # 類的路徑
),
}

區域性檢視中使用

from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from four.utils.auth import APIAuth

class AuthAPIView(APIView):
    authentication_classes = [APIAuth,]
    def get(self,request):
        print('>>>>',request.user)  # AnonymousUser  匿名使用者,假使用者
        print('>>>>',request.auth)  # AnonymousUser  匿名使用者,假使用者
        #>>>> root
        return Response({'msg':'hello'})

3.許可權:Permissions

許可權控制可以限制使用者對於檢視的訪問和對於具體資料物件的訪問。

  • 在執行檢視的dispatch()方法前,會先進行檢視訪問許可權的判斷

  • 在通過get_object()獲取具體物件時,會進行模型物件訪問許可權的判斷

可以在配置檔案中全域性設定預設的許可權管理類

REST_FRAMEWORK = {
    ....
    
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated', # 登入狀態下才能訪問我們的介面,可以通過退出admin後臺之後,你看一下還能不能訪問我們正常的介面就看到效果了
    )
}

如果未指明,則採用如下預設配置

from rest_framework import permissions
'DEFAULT_PERMISSION_CLASSES': (
   'rest_framework.permissions.AllowAny', # 表示任何人都可以進行任何的操作,沒做限制
)

也可以在具體的檢視中通過permission_classes屬性來設定

from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView

class ExampleView(APIView):
    permission_classes = (IsAuthenticated,)
    ...

提供的許可權

  • AllowAny 允許所有使用者

  • IsAuthenticated 僅通過認證的使用者

  • IsAdminUser 僅管理員使用者(可以通過admin建立一個使用者進行測試)

  • IsAuthenticatedOrReadOnly 已經登陸認證的使用者可以對資料進行增刪改操作,沒有登陸認證的只能檢視資料。

示例

from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import RetrieveAPIView

class StudentAPIView(RetrieveAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    authentication_classes = [SessionAuthentication]
    permission_classes = [IsAuthenticated]

自定義許可權

如需自定義許可權,需繼承rest_framework.permissions.BasePermission父類,並實現以下兩個任何一個方法或全部

  • .has_permission(self, request, view)

    是否可以訪問檢視, view表示當前檢視物件

  • .has_object_permission(self, request, view, obj)

    是否可以訪問資料物件, view表示當前檢視, obj為資料物件

例如在當前子應用下,建立一個許可權檔案permissions.py中宣告自定義許可權類:

from rest_framework.permissions import BasePermission

class IsXiaoMingPermission(BasePermission):
    def has_permission(self, request, view):
      
        if( request.user.username == "xiaoming" ):
            return True

檢視函式

from .permissions import IsXiaoMingPermission
class StudentViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    permission_classes = [IsXiaoMingPermission]

4.限流:Throttling

可以對介面訪問的頻次進行限制,以減輕伺服器壓力。

一般用於付費購買次數,投票等場景使用.

可以在配置檔案中,使用DEFAULT_THROTTLE_CLASSESDEFAULT_THROTTLE_RATES進行全域性配置,

REST_FRAMEWORK = {
  
    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle', # 匿名使用者,未登入的
        'rest_framework.throttling.UserRateThrottle' # 經過登入之後的使用者
    ),
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
        'user': '1000/day'
    }
}

DEFAULT_THROTTLE_RATES 可以使用 second, minute, hourday來指明週期。

也可以在具體檢視中通過throttle_classess屬性來配置

from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView

class ExampleView(APIView):
    throttle_classes = (UserRateThrottle,)  # 區域性配置
    ...

可選擇的限流類

1) AnonRateThrottle

限制所有匿名未認證使用者,使用IP區分使用者。

使用DEFAULT_THROTTLE_RATES['anon'] 來設定頻次

2)UserRateThrottle

限制認證使用者,使用User id 來區分。

使用DEFAULT_THROTTLE_RATES['user'] 來設定頻次

3)ScopedRateThrottle (待定...)

限制使用者對於每個檢視的訪問頻次,使用ip或user id,先找的使用者id,沒有設定使用者id的話就會使用ip地址。

示例

全域性配置中設定訪問頻率

    'DEFAULT_THROTTLE_RATES': {
        'anon': '3/minute',
        'user': '10/minute'
    }
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import RetrieveAPIView
from rest_framework.throttling import UserRateThrottle

class StudentAPIView(RetrieveAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    authentication_classes = [SessionAuthentication]
    permission_classes = [IsAuthenticated]
    throttle_classes = (UserRateThrottle,)

ScopedRateThrottle區域性使用示例

# settings.py內容
'DEFAULT_THROTTLE_RATES': {
        'xx': '3/minute',
        'oo': '5/minute',
    },
    
    
# views.py內容

from rest_framework.throttling import ScopedRateThrottle

class StudentAPIView(ListAPIView):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    throttle_classes = [ScopedRateThrottle,]
    throttle_scope = 'xx'

class StudentAPI2View(ListAPIView):
    queryset = models.Student.objects.all()
    serializer_class = StudentModelSerializer
    throttle_classes = [ScopedRateThrottle, ]
    throttle_scope = 'oo'
    
# urls.py內容
    path(r'students/',views.StudentAPIView.as_view()),
    path(r'students2/',views.StudentAPI2View.as_view()),

5.過濾:Filtering

對於列表資料可能需要根據欄位進行過濾,我們可以通過新增django-fitlter擴充套件來增強支援。

pip install django-filter  

在配置檔案中增加過濾後端的設定:

INSTALLED_APPS = [
    ...
    'django_filters',  # 需要註冊應用,
]

REST_FRAMEWORK = {
    ...
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}

 

在檢視中新增filter_fields屬性,指定可以過濾的欄位

class StudentListView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer
    filter_fields = ('age', 'sex')

# 127.0.0.1:8000/four/students/?sex=1

6.排序:OrderingFilter

對於列表資料,REST framework提供了OrderingFilter過濾器來幫助我們快速指明資料按照指定欄位進行排序。

使用方法

在類檢視中設定filter_backends,使用rest_framework.filters.OrderingFilter過濾器,REST framework會在請求的查詢字串引數中檢查是否包含了ordering引數,如果包含了ordering引數,則按照ordering引數指明的排序欄位對資料集進行排序。

前端可以傳遞的ordering引數的可選欄位值需要在ordering_fields中指明。

示例

class StudentListView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    filter_backends = [OrderingFilter]
    ordering_fields = ('id', 'age')

# 127.0.0.1:8000/books/?ordering=-age 
# 必須是ordering=某個值
# -id 表示針對id欄位進行倒序排序
# id  表示針對id欄位進行升序排序

如果需要在過濾以後再次進行排序,則需要兩者結合!

from rest_framework.generics import ListAPIView
from students.models import Student
from .serializers import StudentModelSerializer
from django_filters.rest_framework import DjangoFilterBackend # 需要使用一下它才能結合使用
class Student3ListView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    filter_fields = ('age', 'sex')
    
    # 因為filter_backends是區域性過濾配置,區域性配置會覆蓋全域性配置,所以需要重新把過濾元件核心類再次宣告,
    # 否則過濾功能會失效
    filter_backends = [OrderingFilter,DjangoFilterBackend]
    
    ordering_fields = ('id', 'age')
    
    
    # 針對的是繼承的類中的list方法
    
# 127.0.0.1:8000/books/?sex=1&ordering=-age 

7.分頁:Pagination

REST framework提供了分頁的支援。

我們可以在配置檔案中設定全域性的分頁方式

REST_FRAMEWORK = {
  # 全域性分頁,一旦設定了全域性分頁,那麼我們drf中的檢視擴充套件類裡面的list方法提供的列表頁都會產生分頁的效果。所以一般不用全域性分頁
    'DEFAULT_PAGINATION_CLASS':  'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 100  # 每頁最大資料量
}

也可通過自定義Pagination類,來為檢視新增不同分頁行為。在檢視中通過pagination_class屬性來指明。

class LargeResultsSetPagination(PageNumberPagination):
    page_size = 1000  # 每頁顯示多少條
    #127.0.0.1:8001/students/?page=5&page_size=10
    
    page_size_query_param = 'page_size'
    max_page_size = 10000
    
class BookDetailView(RetrieveAPIView):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer
    pagination_class = LargeResultsSetPagination

注意:如果在檢視內關閉分頁功能,只需在檢視內設定

pagination_class = None

可選分頁器

1.PageNumberPagination

前端訪問網址形式

GET  http://127.0.0.1:8000/students/?page=4

可以在子類中定義的屬性:

  • page_size 每頁數目

  • page_query_param 前端傳送的頁數關鍵字名,預設為"page"

  • page_size_query_param 前端傳送的每頁數目關鍵字名,預設為None

  • max_page_size 前端最多能設定的每頁數量

# 宣告分頁的配置類
from rest_framework.pagination import PageNumberPagination
class StandardPageNumberPagination(PageNumberPagination):
    # 預設每一頁顯示的資料量
    page_size = 2
    # 允許客戶端通過get引數來控制每一頁的資料量
    page_size_query_param = "size"
    max_page_size = 10  # 客戶端通過size指定獲取資料的條數時,最大不能超過多少
    # 自定義頁碼的引數名
    page_query_param = "p"

class StudentAPIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    pagination_class = StandardPageNumberPagination

# 127.0.0.1/four/students/?p=1&size=5

2.LimitOffsetPagination

前端訪問網址形式:其實就是通過偏移量來取資料

GET http://127.0.0.1/four/students/?limit=100&offset=400  # 從下標為400的記錄開始,取100條記錄

可以在子類中定義的屬性:

  • default_limit 預設限制,每頁資料量大小,預設值與PAGE_SIZE設定一致

  • limit_query_param limit引數名,預設'limit' , 可以通過這個引數來改名字

  • offset_query_param offset引數名,預設'offset' ,可以通過這個引數來改名字

  • max_limit 最大limit限制,預設None, 無限制

from rest_framework.pagination import LimitOffsetPagination
class StandardLimitOffsetPagination(LimitOffsetPagination):
    # 預設每一頁查詢的資料量,類似上面的page_size
    default_limit = 2
    limit_query_param = "size"  # 預設是limit
    offset_query_param = "start"  # 預設是offset

class StudentAPIView(ListAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    # 呼叫頁碼分頁類
    # pagination_class = StandardPageNumberPagination
    # 呼叫查詢偏移分頁類
    pagination_class = StandardLimitOffsetPagination

8.異常處理:Exception

一個簡單的示例

class APIError(Exception):
    pass

class Student2APIView(APIView):
    def get(self,request,pk):
        try:
            instance = Student.objects.get(pk=pk)
        except Student.DoesNotExist:
            raise APIError('自定義API錯誤')
            return Response({"message":"訪問的商品已經下架~"})

        serializer = StudentModelSerializer(instance=instance)
        return Response(serializer.data)

REST framework提供了異常處理,我們可以自定義異常處理函式。

可以建立一個utils資料夾,裡面放一個exceptions.py檔案,名字隨便寫,然後寫下面的內容

from rest_framework.views import exception_handler

def custom_exception_handler(exc, context): # 自定義的錯誤處理函式
      ”“”
        exc錯誤物件
      context 異常發生時的一些上下文資訊
    “”“
    # 先呼叫REST framework預設的異常處理方法獲得標準錯誤響應物件
    response = exception_handler(exc, context) # 這個函式是drf提供的,它處理了一些錯誤,但是如果它處理不了的,它會返回None,所以,如果是None的話,我們需要自己來處理錯誤

    # 在此處補充自定義的異常處理
    if response is None:
          if isinstance(exc,APIError)
        # 這裡就可以記錄錯誤資訊了,一般記錄到檔案中,可以使用日誌系統來進行記錄
        # return Respose({'msg':'自定義API錯誤了'})
        response.data['status_code'] = response.status_code

    return response

在配置檔案中還要宣告自定義的異常處理

REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}

如果未宣告,會採用預設的方式,如下

REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
}

處理關於資料庫的異常

from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework import status
from django.db import DatabaseError

def exception_handler(exc, context):
    response = drf_exception_handler(exc, context)

    if response is None:
        view = context['view'] # 出錯的方法或者函式名稱
        if isinstance(exc, DatabaseError):
            print('[%s]: %s' % (view, exc))
            response = Response({'detail': '伺服器內部錯誤'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)

    return response

drf定義的異常

  • APIException 所有異常的父類

  • ParseError 解析錯誤

  • AuthenticationFailed 認證失敗

  • NotAuthenticated 尚未認證

  • PermissionDenied 許可權決絕

  • NotFound 未找到

  • MethodNotAllowed 請求方式不支援

  • NotAcceptable 要獲取的資料格式不支援

  • Throttled 超過限流次數

  • ValidationError 校驗失敗

也就是說,上面列出來的異常不需要我們自行處理了,很多的沒有在上面列出來的異常,就需要我們在自定義異常中自己處理了。

9.自動生成介面文件

REST framework可以自動幫助我們生成介面文件。

介面文件以網頁的方式呈現。

自動介面文件能生成的是繼承自APIView及其子類的檢視。

1.安裝依賴檔案

pip install coreapi

2.設定介面文件訪問路徑

在總路由中新增介面文件路徑。

文件路由對應的檢視配置為rest_framework.documentation.include_docs_urls

引數title為介面文件網站的標題。

from rest_framework.documentation import include_docs_urls

urlpatterns = [
    ...
    path('docs/', include_docs_urls(title='站點頁面標題'))
]

如果報錯了下面的錯誤,說明我們缺少一個依賴,配置一下就行了

`'AutoSchema' object has no attribute 'get_link'`

配置

REST_FRAMEWORK = {
    ...
    'DEFAULT_SCHEMA_CLASS': "rest_framework.schemas.AutoSchema",

}

 

相關文章