1. 程式人生 > 實用技巧 >drf之過濾分頁異常

drf之過濾分頁異常

一 過濾Filtering

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

1
pip install django-filter

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

1
2
3
4
5
6
7
8
9
INSTALLED_APPS = [
...
'django_filters', # 需要註冊應用,
]

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

}

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

1
2
3
4
5
6
classStudentListView(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過濾器,REST framework會在請求的查詢字串引數中檢查是否包含了ordering引數,如果包含了ordering引數,則按照ordering引數指明的排序欄位對資料集進行排序。

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

示例:

1
2
3
4
5
6
7
8
9
classStudentListView(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欄位進行升序排序

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

1
2
3
4
5
6
7
8
9
10
11
12
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提供了分頁的支援。

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

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

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

1
2
3
4
5
6
7
8
classLargeResultsSetPagination(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

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

1
pagination_class = None

可選分頁器

1) PageNumberPagination

前端訪問網址形式:

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

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

  • page_size 每頁數目
  • page_query_param 前端傳送的頁數關鍵字名,預設為”page”
  • page_size_query_param 前端傳送的每頁數目關鍵字名,預設為None
  • max_page_size 前端最多能設定的每頁數量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# APIView
from rest_framework.pagination import PageNumberPagination
# 一 基本使用:url=url=http://127.0.0.1:8000/pager/?page=2&size=3,size無效
classPager(APIView):
defget(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條
classMypage(PageNumberPagination):
page_size = 2
page_query_param = 'page'
# 定製傳參
page_size_query_param = 'size'
# 最大一頁的資料
max_page_size = 5
classPager(APIView):
defget(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
classStandardPageNumberPagination(PageNumberPagination):
# 預設每一頁顯示的資料量
page_size = 2
# 允許客戶端通過get引數來控制每一頁的資料量
page_size_query_param = "size"
max_page_size = 10
# 自定義頁碼的引數名
page_query_param = "p"

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

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

2)LimitOffsetPagination

前端訪問網址形式:

1
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# APIView
# http://127.0.0.1:8000/pager/?offset=4&limit=3
from rest_framework.pagination import LimitOffsetPagination
# 也可以自定製,同簡單分頁
classPager(APIView):
defget(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
classStandardLimitOffsetPagination(LimitOffsetPagination):
# 預設每一頁查詢的資料量,類似上面的page_size
default_limit = 2
limit_query_param = "size"
offset_query_param = "start"

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

3)CursorPagination

前端訪問網址形式:

1
GET http://127.0.0.1/four/students/?cursor=cD0xNQ%3D%3D

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

  • cursor_query_param:預設查詢欄位,不需要修改
  • page_size:每頁數目
  • ordering:按什麼排序,需要指定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#APIView
from rest_framework.pagination import CursorPagination
# 看原始碼,是通過sql查詢,大於id和小於id
classPager(APIView):
defget(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
classMyCursorPagination(CursorPagination):
page_size=2
ordering='-id'
from rest_framework.generics import ListAPIView
classAuthorListView(ListAPIView):
serializer_class = serializers.AuthorModelSerializer
queryset = models.Author.objects.filter(is_delete=False)
pagination_class =MyCursorPagination

應用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

classMyLimitOffsetPagination(LimitOffsetPagination):
default_limit = 2
# 最大一頁的資料
max_limit = 5
classMyCursorPagination(CursorPagination):
page_size=2
ordering='-id'
from rest_framework.generics import ListAPIView
classAuthorListView(ListAPIView):
serializer_class = serializers.AuthorModelSerializer
queryset = models.Author.objects.filter(is_delete=False)
pagination_class =MyCursorPagination

四 異常處理 Exceptions

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

4.1 使用方式

1
2
3
4
5
6
7
8
9
10
11
from rest_framework.views import exception_handler

defcustom_exception_handler(exc, context):
# 先呼叫REST framework預設的異常處理方法獲得標準錯誤響應物件
response = exception_handler(exc, context)

# 在此處補充自定義的異常處理
if response isNone:
response.data['status_code'] = response.status_code

return response

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

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

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

rest_frame/settings.py

1
2
3
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
}

4.2 案例

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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 校驗失敗

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