day74:drf:drf其他功能:認證/權限/限流/過濾/排序/分頁/異常處理&自動生成接口文檔

目錄

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  //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 //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",

}