【九】DRF之过滤排序异常

发布时间 2023-07-31 12:19:24作者: Chimengmeng

【一】过滤(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

【二】排序(Ordering)

  • 对于列表数据
    • 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】可选分页器

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)

# 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)

自定制(APIView)

# 二 自定制 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

#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

# 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

#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

#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

# 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

【2】应用

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提供了异常处理,我们可以自定义异常处理函数。

【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'
}

【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 校验失败

也就是说,很多的没有在上面列出来的异常,就需要我们在自定义异常中自己处理了。