1. 程式人生 > 實用技巧 >-drf-過濾排序分頁異常處理

-drf-過濾排序分頁異常處理

一 過濾Filtering

1 內建過濾

# 模組匯入
from rest_framework.filters import SearchFilter
1.過濾目的:篩選查詢結果(模糊匹配,只要含有就匹配出來)

2.內建篩選使用
		-在檢視類中配置
  			filter_backends =[SearchFilter,]
    		# 匹配所有欄位進行過濾篩選
      -http://127.0.0.1:8000/books/?search=i        
        # 只要有一個物件中的不論任何欄位模糊匹配到i,就過濾出來
        filter_backends = [SearchFilter, ]
    		search_fields = ('title',)
        # 匹配title欄位進行過濾篩選
      -http://127.0.0.1:8000/books/?search=i
        # 這樣是隻要有一個物件中的title欄位模糊匹配到i,就過濾出來
        

2 第三方擴充套件的過濾功能

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

# 安裝
pip install django-filter  :最新版本(2.4.0)要跟django2.2以上搭配

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

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

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

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

# 匯入模組
from django_filters.rest_framework import DjangoFilterBackend
class BookInfo(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookModelSerializer
    filter_backends = [DjangoFilterBackend, ]
    filter_fields = ('title', 'price') # 與內建的search方法不同
    
# 輸入的url
-http://127.0.0.1:8000/books/?title=i 或 http://127.0.0.1:8000/books/?price=1
        
# 見名知意:可以直接在?欄位=匹配內容 的方式在book表中指定的該欄位模糊匹配過濾出資料物件

二 排序

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

使用方法:

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

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

舉例:

# 匯入模組
from rest_framework.filters import OrderingFilter
class BookInfo(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookModelSerializer
    filter_backends = [OrderingFilter, ] 
    ordering_fields = ('price',) # 指定欄位,預設升序排序

-http://127.0.0.1:8000/books/?ordering=price # 也需要在url配置相應的引數
    
# ordering_fields = ('-price',)   # 降序排序

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

class BookInfo(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookModelSerializer
    throttle_classes = [AnonRateThrottle, UserRateThrottle]
    filter_backends = [DjangoFilterBackend, OrderingFilter]
    filter_fields = ('publish',)
    ordering_fields = ('price',)

-http://127.0.0.1:8000/books/?publish=1&ordering=price
    # 在過濾出出版社publish=1後,將過濾物件以Price升序排序

三 分頁Pagination

REST framework提供了分頁的支援。

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

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS':  'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 100  # 每頁數目
}

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

pagination_class = None

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

# 匯入模組
from rest_framework.pagination import PageNumberPagination
from rest_framework.generics import ListAPIView

# myauth.py
class ResultSetPagination(PageNumberPagination):
    page_size = 2  # 每頁顯示的條數
    page_query_param = 'page'  # 前端返回來查詢頁數的關鍵字
    max_page_size = 30  # 前端顯示的最多的頁數

# views.py
# path('books/', views.BookInfo.as_view({"get": "list"}))
class BookInfo(ListModelMixin, GenericViewSet): # 只進行查詢所有資料的介面不用寫get方法
  																							# 路由已經進行了配置
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookModelSerializer
    pagination_class = myauth.ResultSetPagination

1 PageNumberPagination:普通分頁

前端訪問網址形式:

GET  http://127.0.0.1:8000/books/?page=3&size=1

找到第三頁從上到下數的第一條資料

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

  • page_szie 每頁的條數,後端預設的,如果前端有指定按照前端指定的
  • page_query_param 前端傳送頁數的關鍵字名,預設為“page”
  • page_size_query_param 前端傳送的每頁條數的關鍵字名,預設為None
  • max_page_size 前端最多能設定的分頁的總數
from rest_framework.pagination import PageNumberPagination
class ResultSetPagination(PageNumberPagination):
    page_size = 2  # 每頁顯示的條數
    page_query_param = 'page'  # 前端返回來查詢頁數的關鍵字
    max_page_size = 30  # 前端顯示的最多的頁數
    page_size_query_param = 'size' # 前端傳送的每頁條數的關鍵字名
    
# views.py
class BookInfo(ListModelMixin, GenericViewSet):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookModelSerializer
    pagination_class = myauth.ResultSetPagination

APIView的分頁模式

-新建一個類,繼承普通分頁,重寫四個屬性
    -檢視類寫法如下
	class StudentApiView(APIView):
        def get(self,request):
            student_list=Student.objects.all()
            page=MyPageNumberPagination()# 例項化得到物件
            # 只需要換不同的分頁類即可
            res=page.paginate_queryset(student_list,request,self)# 開始分頁
            ser=StudentSerializer(res,many=True)
            return page.get_paginated_response(ser.data) # 返回資料

2 LimitOffsetPagination:偏移分頁

前端訪問網址形式:

GET http://127.0.0.1/books/?limit=2&offset=4

找到第四條資料然後以兩條資料為一頁進行分頁(起始資料從5開始不包含4)

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

  • deault_limit 每頁條數,後端預設的,如果前端有指定按照前端指定的
  • limit_query_param 前端指定每頁查詢多少條的關鍵字
  • offset_query_param 前端指定查詢的起始位置的關鍵字
  • max_limit 查詢時最多返回多少條
from rest_framework.pagination import LimitOffsetPagination
# auth.py
class LimitSetPagination(LimitOffsetPagination):
    default_limit = 3
    limit_query_param = 'limit'
    offset_query_param = 'offset'
    
# views.py
class BookInfo(ListModelMixin, GenericViewSet):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookModelSerializer
    pagination_class = myauth.LimitSetPagination

APIView的分頁模式

class  Pager(APIView):
    def get(self,request,*args,**kwargs):
        # 獲取所有資料
        ret=models.Book.objects.all()
        # 建立分頁物件
        page=LimitOffsetPagination()
        # 在資料庫中獲取分頁的資料
        page_list=page.paginate_queryset(ret,request,view=self)
        # 對分頁進行序列化
        ser=BookSerializer1(instance=page_list,many=True)
        # return page.get_paginated_response(ser.data)
        return Response(ser.data)

3 CursorPagination:遊標分頁

速度快:檢索資料方式,是在當前頁面向上或向下檢索

GET http://127.0.0.1:8000/books/?cursor=bz0z&page=3

# cursor遊標標識跳轉的當前頁面,遊標卡在當前頁面的上下,所以查詢速度很快,直接在當前遊標向上或向下檢索。但是也限制了不能直接跳轉到其他頁面只能上下。

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

  • cursor_query_param: 預設查詢欄位,不需要修改,前端指定查詢的關鍵字
  • page_size:每頁數目,後端預設的,如果前端有指定按照前端指定的
  • ordering:按什麼排序,需要指定
  • page_size_query_param 前端傳送的每頁條數的關鍵字名,預設為None
from rest_framework.pagination import CursorPagination
# auth.py
class CursorSetPagination(CursorPagination):
    page_size = 2  # 預設頁面條數
    ordering = 'id'
    cursor_query_param = 'cursor'  # 預設cursor 可以不寫
    page_size_query_param = 'page'  # 前端指定頁面條數,則按前端指定的來
    
# views.py
class BookInfo(ListModelMixin, GenericViewSet):
    queryset = models.Book.objects.all()
    serializer_class = serializer.BookModelSerializer
    pagination_class = myauth.CursorSetPagination

APIView分頁模式

class  Pager(APIView):
    def get(self,request,*args,**kwargs):
        # 獲取所有資料
        ret=models.Book.objects.all()
        # 建立分頁物件
        page=CursorPagination()
        page.ordering='nid'
        # 在資料庫中獲取分頁的資料
        page_list=page.paginate_queryset(ret,request,view=self)
        # 對分頁進行序列化
        ser=BookSerializer1(instance=page_list,many=True)
        # 可以避免頁碼被猜到
        return page.get_paginated_response(ser.data)

四 異常處理Exceptions

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

1 使用方式

from rest_framework.views import exception_handler

def custom_exception_handler(exc, context):
    print(str(exc))
    # 先呼叫drf預設的異常處理方式獲得標準錯誤響應物件
    response = exception_handler(exc, context)
    # 在此處補充自定的異常處理,返回前端
    if response is None:  # 沒有返回值代表有異常

        response = Response({'detail': str(exc)})
    return response

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

REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'app10.myauth.custom_exception_handler',
}

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

rest_frame/settings.py

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

2 補充上處理關於資料庫的異常

from rest_framework.views import exception_handler
from rest_framework.response import Response
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']
        print('[%s]: %s' % (view, exc))
        if isinstance(exc, DatabaseError):
            response = Response({'detail': '伺服器內部錯誤'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)
        else:
            response = Response({'detail': '未知錯誤'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    return response
  
# 在setting.py中配置
REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'app01.ser.exception_handler'
}

3 REST framework定義的異常

  • APIException 所有異常的父類
  • ParseError 解析錯誤
  • AuthenticationFailed 認證失敗
  • NotAuthenticated 尚未認證
  • PermissionDenied 許可權決絕
  • NotFound 未找到
  • MethodNotAllowed 請求方式不支援
  • NotAcceptable 要獲取的資料格式不支援
  • Throttled 超過限流次數
  • ValidationError 校驗失敗