07 過濾排序分頁異常處理
對於列表資料可能需要根據欄位進行過濾,我們可以通過新增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
二 排序
對於列表資料,REST framework提供了OrderingFilter過濾器來幫助我們快速指明資料按照指定欄位進行排序。
使用方法:
在類檢視中設定filter_backends,使用rest_framework.filters.OrderingFilter
前端可以傳遞的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
# -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 = [OrderingFilter,DjangoFilterBackend]
ordering_fields = ('id', 'age')
三 分頁Pagination
REST framework提供了分頁的支援。
我們可以在配置檔案中設定全域性的分頁方式,如:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 100 # 每頁數目
}
也可通過自定義Pagination類,來為檢視新增不同分頁行為。在檢視中通過pagination_clas
屬性來指明。
class LargeResultsSetPagination(PageNumberPagination):
page_size = 1000
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 前端最多能設定的每頁數量
# APIView from rest_framework.pagination import PageNumberPagination # 一 基本使用:url=url=http://127.0.0.1:8000/pager/?page=2&size=3,size無效 class Pager(APIView): def get(self,request,*args,**kwargs): # 獲取所有資料 ret=models.Book.objects.all() # 建立分頁物件 page=PageNumberPagination() # 在資料庫中獲取分頁的資料 page_list=page.paginate_queryset(ret,request,view=self) # 對分頁進行序列化 ser=BookSerializer1(instance=page_list,many=True) return Response(ser.data) # 二 自定製 url=http://127.0.0.1:8000/pager/?page=2&size=3 # size=30,無效,最多5條 class Mypage(PageNumberPagination): page_size = 2 page_query_param = 'page' # 定製傳參 page_size_query_param = 'size' # 最大一頁的資料 max_page_size = 5 class Pager(APIView): def get(self,request,*args,**kwargs): # 獲取所有資料 ret=models.Book.objects.all() # 建立分頁物件 page=Mypage() # 在資料庫中獲取分頁的資料 page_list=page.paginate_queryset(ret,request,view=self) # 對分頁進行序列化 ser=BookSerializer1(instance=page_list,many=True) # return Response(ser.data) # 這個也是返回Response物件,但是比基本的多了上一頁,下一頁,和總資料條數(瞭解即可) return page.get_paginated_response(ser.data) #ListAPIView # 宣告分頁的配置類 from rest_framework.pagination import PageNumberPagination class StandardPageNumberPagination(PageNumberPagination): # 預設每一頁顯示的資料量 page_size = 2 # 允許客戶端通過get引數來控制每一頁的資料量 page_size_query_param = "size" max_page_size = 10 # 自定義頁碼的引數名 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
可以在子類中定義的屬性:
-
default_limit 預設限制,預設值與
PAGE_SIZE
設定一直 -
limit_query_param limit引數名,預設’limit’
-
offset_query_param offset引數名,預設’offset’
-
max_limit 最大limit限制,預設None
# APIView # http://127.0.0.1:8000/pager/?offset=4&limit=3 from rest_framework.pagination import LimitOffsetPagination # 也可以自定製,同簡單分頁 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) #ListAPIView from rest_framework.pagination import LimitOffsetPagination class StandardLimitOffsetPagination(LimitOffsetPagination): # 預設每一頁查詢的資料量,類似上面的page_size default_limit = 2 limit_query_param = "size" offset_query_param = "start" class StudentAPIView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer # 呼叫頁碼分頁類 # pagination_class = StandardPageNumberPagination # 呼叫查詢偏移分頁類 pagination_class = StandardLimitOffsetPagination
3)CursorPagination
前端訪問網址形式:
GET http://127.0.0.1/four/students/?cursor=cD0xNQ%3D%3D
可以在子類中定義的屬性:
-
cursor_query_param:預設查詢欄位,不需要修改
-
page_size:每頁數目
-
ordering:按什麼排序,需要指定
#APIView from rest_framework.pagination import CursorPagination # 看原始碼,是通過sql查詢,大於id和小於id 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) # ListAPIView class MyCursorPagination(CursorPagination): page_size=2 ordering='-id' from rest_framework.generics import ListAPIView class AuthorListView(ListAPIView): serializer_class = serializers.AuthorModelSerializer queryset = models.Author.objects.filter(is_delete=False) pagination_class =MyCursorPagination
應用
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination class MyPageNumberPagination(PageNumberPagination): page_size = 2 page_query_param = 'page' # 定製傳參 page_size_query_param = 'size' # 最大一頁的資料 max_page_size = 5 class MyLimitOffsetPagination(LimitOffsetPagination): default_limit = 2 # 最大一頁的資料 max_limit = 5 class MyCursorPagination(CursorPagination): page_size=2 ordering='-id' from rest_framework.generics import ListAPIView class AuthorListView(ListAPIView): serializer_class = serializers.AuthorModelSerializer queryset = models.Author.objects.filter(is_delete=False) pagination_class =MyCursorPagination
四 異常處理 Exceptions
REST framework提供了異常處理,我們可以自定義異常處理函式。
4.1 使用方式
from rest_framework.views import exception_handler def custom_exception_handler(exc, context): # 先呼叫REST framework預設的異常處理方法獲得標準錯誤響應物件 response = exception_handler(exc, context) # 在此處補充自定義的異常處理 if response is None: response.data['status_code'] = response.status_code return response
在配置檔案中宣告自定義的異常處理
REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler' }
如果未宣告,會採用預設的方式,如下
rest_frame/settings.py
REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler' }
4.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' }
4.3 REST framework定義的異常
-
APIException 所有異常的父類
-
ParseError 解析錯誤
-
AuthenticationFailed 認證失敗
-
NotAuthenticated 尚未認證
-
PermissionDenied 許可權決絕
-
NotFound 未找到
-
MethodNotAllowed 請求方式不支援
-
NotAcceptable 要獲取的資料格式不支援
-
Throttled 超過限流次數
-
ValidationError 校驗失敗