1. 程式人生 > 其它 >07 過濾排序分頁異常處理

07 過濾排序分頁異常處理

一 過濾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

二 排序

對於列表資料,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
# -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 校驗失敗

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