Django-rest-framework过滤,排序,分页,异常处理

一.过滤组件 Filter

  • 过滤是对多条数据进行筛选, 所以, 它只针对于 list(获取所有)
  • 在请求路径中携带过滤条件, 对查询的结果进行过滤

1.drf 内置的过滤组件 SearchFilter

  • models.py
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.IntegerField()
  • serializer.py
from rest_framework import serializers
from drf_test import models

class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = "__all__"
  • views.py
class BookView2(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer

    # 导入SearchFilter组件
    from rest_framework.filters import SearchFilter
    # 指定组件
    filter_backends = [SearchFilter]
    # 指定搜索字段
    search_fields = ['title', 'price']
  • 使用Postman来测试
# 127.0.0.1:8000/book2/?search=12
# 127.0.0.1:8000/book2/?search=2  (模糊查询)
# 127.0.0.1:8000/book2/?title=雪  (模糊查询)

img

  • 总结
# 支持模糊查询
# 只能使用 search 来进行条件指定
# 可以使用 "," 来隔离多个条件来进行查询
# 不能查询外键字段,报错 : Related Field got invalid lookup: icontains

2.第三方过滤组件 Django-filter

  • 安装 : pip3 install django-filter
  • 注册 : 在 setting.py 文件中注册app
INSTALLED_APPS = [
    ...
    'django_filters',
]
  • 全局配置
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
}
  • 局部配置
# 导入过滤器组件
from django_filters.rest_framework import DjangoFilterBackend

# 视图类中书写指定
filter_backends = [DjangoFilterBackend,]
filter_fields = '__all__'  # 所有字段
filter_fields = ['title',..]  # 指定某几个字段
  • views.py 中使用
from django_filters.rest_framework import DjangoFilterBackend

class BookView2(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer

    # 指定第三方组件
    filter_backends = [DjangoFilterBackend,]
    # 指定字段,注意是 filter_fields,不是 search_fields
    filter_fields = ['title', 'price']
  • Postman 测试
# 127.0.0.1:8000/book2/?title=萨嘎达
# 127.0.0.1:8000/book2/?price=2  (不支持模糊查询)
# 127.0.0.1:8000/book2/?price=34&title=阿嘎达

img

  • 总结
# 可以指定字段名进行查询 (还有一些其他强大功能,如:通过时间过滤,(后边介绍))
# 不支持模糊查询
# 针对django内置搜索组件的拓展, 在django内置的基础之上还拓展了外键字段的过滤功能

3.自定义过滤类

  • 创建一个存放过滤类的文件
  • 在文件中书写自定义的过滤器类 : customfilter.py
  • 继承 BaseFilterBackend , 必须重写 filter_queryset 方法
from rest_framework.filters import BaseFilterBackend
from django.db.models import F,Q

# 自定义过滤类(继承BaseFilterBackend)
class MyFilter(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        price = request.GET.get('price')
        title = request.GET.get('title')
        # 匹配的逻辑代码自己定制
        if title and price:
            # contains : 包含(模糊匹配)
            queryset = queryset.filter(Q(title__contains=title)|Q(price__contains=price))  
        if title:
            queryset = queryset.filter(title__contains=title)
        if price:
            queryset = queryset.filter(price__contains=price)
        return queryset
  • views.py
# 导入自定义过滤类
from drf_test.customfilter.customfilter import MyFilter

class BookView2(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer

    filter_backends = [MyFilter,]
  • Postman 测试
# 127.0.0.1:8000/book2/?title=萨嘎达
# 127.0.0.1:8000/book2/?price=2  (模糊查询)
# 127.0.0.1:8000/book2/?price=2&title=雪  (多个条件模糊查询)

img

  • 总结
# 可以自定义匹配逻辑
# 自定义模糊匹配等等, 扩展性比较强

4.自定义过滤类高级版代码示例

# 示例代码一:
class ReagentListFilter(FilterSet):
    '''
    获取所有的试剂名称
    '''
    reagent_name = django_filters.CharFilter(method="filter_reagent_name")

    class Meta:
        model = models.Reagent
        fields = ["reagent_name"]

    @staticmethod
    def filter_reagent_name(queryset, name, values):
        try:
            return queryset.filter(reagent_name__icontains=values)
        except JSONDecodeError as E:
            return queryset

# 示例代码二:
class CalibrationFilter(FilterSet):
    '''
    区间过滤器
    '''
    def check_permission(self, request, query_condition):
        filter_condition = dict()
        # 获取用户基本信息
        user_id, org_post_id, organization_id, org_type = get_user_info(request)
        o_user2binding = get_binding_info(user_id, org_post_id, organization_id, org_type)
        '''
        - [ ] (1**) 奥普-全体 无限制.
        - [ ] (2**) 医院-全体 所属医院的定标, 见 UserInfo.organization.
        - [ ] (3**) 经销商-全体 所属公司对应仪器的定标, 见 Instrument.customer.
        '''
        if org_type == 2:
            filter_condition["hospital__in"] = o_user2binding
            query_condition["hospital__in"] = o_user2binding
        return query_condition

    def filter_queryset(self, queryset):
        query_condition = dict()
        for keys, values in self.request.query_params.items():
            if not values:
                continue
            if keys == "start_time":
                query_condition["corrected_date__gte"] = values
            if keys == "end_time":
                query_condition["corrected_date__lte"] = values
            if keys == "hospital_name":
                query_condition["hospital__hospital_full_name__icontains"] = values
        query_condition = self.check_permission(self.request, query_condition)
        return queryset.filter(~Q(hospital=None), **query_condition)

# 示例代码三:
class ReagentListFilter(FilterSet):
    '''
    获取所有的试剂名称
    '''
    reagent_name = django_filters.CharFilter(method="filter_reagent_name")

    class Meta:
        model = models.Reagent
        fields = ["reagent_name"]

    @staticmethod
    def filter_reagent_name(queryset, name, values):
        try:
            return queryset.filter(reagent_name__icontains=values)
        except JSONDecodeError as E:
            return queryset

二.排序组件 Ordering

1.配置

  • 全局配置
REST_FRAMEWORK = {
        # 过滤
        # 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
        # 排序(key都是一样的)
        # 'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.OrderingFilter'),
        # 过滤和排序放一起
        'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend', 'rest_framework.filters.OrderingFilter'),
        }
  • 局部配置
# 导入排序组件
from rest_framework.filters import OrderingFilter

# 在视图类中使用
filter_backends = [OrderingFilter,]
ordering_fields="__all__"

2.使用

  • views.py
from rest_framework.filters import OrderingFilter

class BookView2(ModelViewSet):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer

    filter_backends = [OrderingFilter]
    ordering_fields="__all__"
  • Postman 测试
# 127.0.0.1:8000/book2/?ordering=price   # 默认升序
# 127.0.0.1:8000/book2/?ordering=-price  # - 号,反序
# 127.0.0.1:8000/book2/?ordering=-price,title  # 若第一个条件重复可以继续使用第二个天剑进行
# 127.0.0.1:8000/book2/?ordering=-price,-title

img

3.注意点

  • 过滤和排序组件的全局配置使用的都是同一个Key, 任何一个组件局部进行单一的配置都会覆盖全局的配置

img

三.异常处理 Exception

REST framework 提供了异常处理, 错误有时候被 drf 捕获并处理, 有时候被 Django 捕获处理, 对于他们的处理可能不太符合我们的需求, 那么我们可以自定义异常处理函数

1.异常处理源码分析

  • 自定义异常处理之前先进源码看看其异常如何处理的
  • 步骤 : APIView---->dispatch---->try---->handle_exception

img

  • 再点击handle_exception源码进行查看
def handle_exception(self, exc):
    """
    Handle any exception that occurs, by returning an appropriate response,
    or re-raising the error.
    """
    # exc是捕获到的异常,判断该异常是不是后边两种异常的对象,如果是则交给drf处理
    if isinstance(exc, (exceptions.NotAuthenticated,
                        exceptions.AuthenticationFailed)):
        # WWW-Authenticate header for 401 responses, else coerce to 403
        auth_header = self.get_authenticate_header(self.request)

        if auth_header:
            exc.auth_header = auth_header
        else:
            exc.status_code = status.HTTP_403_FORBIDDEN
    # 该方法返回 self.settings.EXCEPTION_HANDLER(下面有图)
    # 默认指向 rest_framework.views.exception_handler(下面有图)
    # 得到异常处理函数 exception_handler (如果自定义了,找到自定义的,没有则找默认的)
    exception_handler = self.get_exception_handler()
    # 该方法返回的是异常内容(字典), 赋值给context(下面有图)
    context = self.get_exception_handler_context()
    # 执行异常函数,处理异常(重点)
    response = exception_handler(exc, context)
    # 如果response是空,说明没有捕获到异常
    if response is None:
        self.raise_uncaught_exception(exc)

    response.exception = True
    return response

get_exception_handler( ) 方法获取异常对象

img

drf 的 settings.py 配置文件中 EXCEPTION_HANDLER 的指向

img

get_exception_handler_context( ) 方法获取异常内容

img

  • 我们再查看 exception_handler 默认异常函数如何处理异常的
def exception_handler(exc, context):
    """
    Returns the response that should be used for any given exception.

    By default we handle the REST framework `APIException`, and also
    Django's built-in `Http404` and `PermissionDenied` exceptions.

    Any unhandled exceptions may return `None`, which will cause a 500 error
    to be raised.
    """
    # 上面的注释大致 : 会处理REST框架的APIException异常
    # Django内置的Http404和PermissionDenied异常
    # 任何未处理的异常都可能返回None,这会引发500错误(我们可以处理该异常!)

    # Http404异常处理
    if isinstance(exc, Http404):
        exc = exceptions.NotFound()
    # PermissionDenied异常处理
    elif isinstance(exc, PermissionDenied):
        exc = exceptions.PermissionDenied()
    # APIException异常处理
    if isinstance(exc, exceptions.APIException):
        headers = {}
        if getattr(exc, 'auth_header', None):
            headers['WWW-Authenticate'] = exc.auth_header
        if getattr(exc, 'wait', None):
            headers['Retry-After'] = '%d' % exc.wait

        if isinstance(exc.detail, (list, dict)):
            data = exc.detail
        else:
            data = {'detail': exc.detail}

        set_rollback()
        return Response(data, status=exc.status_code, headers=headers)
    # drf未捕获到能处理的异常返回 None
    return None

经过上面的分析, 我们知道 : 对捕获到的异常, 都会被 exception_handler(exc, context) 函数处理, 我们只需要重写该函数, 就可以让其运行我们写的函数来进行异常的处理

2.自定义异常处理

通过翻译exception_handler的注释我们知道的是对于 drf 处理不了的异常会返回 None, 引发500错误, 我们的思路路就是 drf 能处理的异常还是交给它处理, 它不能处理的异常我们再来处理

  • 重写 exception_handler(exc, context) 函数 (新建一个异常处理的文件 : customecception.py)
from rest_framework.response import Response
from rest_framework.views import exception_handler

def custom_exception_handler(exc, context):
    # 先交给 drf 默认的异常处理, 处理不了了(返回None)再我们处理
    response = exception_handler(exc, context)

    print(response)  # None
    print(exc)  # division by zero(异常)
    print(context.get('view'))  # <drf_test.views.TestView object at 0x000002482D0C0FC8>
    print(context.get('request').get_full_path())  # /test/(异常路径)

    # 如果是None, 我们就更细粒度的对异常进行处理并进行记录日志
    if not response:
        # 超出索引异常
        if isinstance(exc, IndexError):
            response = Response({'status': 5001, 'msg': '越界异常'})
        # 除数为零异常 (4/0)
        elif isinstance(exc, ZeroDivisionError):
            response = Response({'status': 5002, 'msg': '除数零异常'})
        elif isinstance(exc, TypeError):
            response = Response({'status': 5002, 'msg': '类型异常'})
        elif isinstance(exc, AttributeError):
            response = Response({'status': 5002, 'msg': '属性异常'})
        else:
            response = Response({'status': 5000, 'msg': '没有权限'})
    # 记录日志操作(省略...)
    return response
  • 在 setting.py 文件中的 drf 配置指定自己的异常类
REST_FRAMEWORK = {
    # 自定义异常处理
    'EXCEPTION_HANDLER': 'drf_test.exception.customexception.custom_exception_handler'
}

# 如果未配置自定义的,将会使用默认的
REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
}
  • 在 views.py 中写一些错误的逻辑进行测试
class TestView(APIView):
    def get(self,request):
        x = 4/0
        print(x)
        return Request()

class TestView2(APIView):
    def get(self,request):
        li = [1,2,3]
        print(li[20])
        return Request()
  • urls.py
path('test/', views.TestView.as_view()),
path('test2/', views.TestView2.as_view()),
  • Postman 测试

img

3.REST framework定义的异常

  • APIException 所有异常的父类
  • ParseError 解析错误
  • AuthenticationFailed 认证失败
  • NotAuthenticated 尚未认证
  • PermissionDenied 权限决绝
  • NotFound 未找到
  • MethodNotAllowed 请求方式不支持
  • NotAcceptable 要获取的数据格式不支持
  • Th`rottled 超过限流次数
  • ValidationError 校验失败

drf 处理不了的其他异常, 就需要我们自定义异常处理

4.自定义异常处理方便copy版

# -*- coding:utf-8 -*-
'''
全局异常类
'''
import traceback
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
# from dimension.logger import getlogger
from logging import getLogger
from rest_framework import exceptions
from rest_framework.exceptions import APIException as DRFAPIException, AuthenticationFailed

logger = getLogger(__name__)
from rest_framework.views import set_rollback
from .json_response import ErrorResponse

def exception_handler(exc, context):
    response = drf_exception_handler(exc, context)
    if response is None:  # 有异常
        view = context['view']
        if isinstance(exc, DatabaseError):
            # 记录服务器异常
            logger.critical('%s' % exc)
            response = Response({"code": 500, 'detail': '服务器内部错误', "异常原因": exc},
                                status=status.HTTP_507_INSUFFICIENT_STORAGE)
        else:
            logger.critical('%s' % exc)
            response = Response({"code": 500, 'detail': '未知错误', "异常原因": exc},
                                status=status.HTTP_500_INTERNAL_SERVER_ERROR)
    return response

class APIException(Exception):
    """
    通用异常:(1)用于接口请求是抛出移除, 此时code会被当做标准返回的code, message会被当做标准返回的msg
    """

    def __init__(self, code=201, message='API异常', args=('API异常',)):
        self.args = args
        self.code = code
        self.message = message

    def __str__(self):
        return self.message

class GenException(APIException):
    pass

class FrameworkException(Exception):
    """
    框架异常、配置异常等
    """

    def __init__(self, message='框架异常', *args: object, **kwargs: object) -> None:
        super().__init__(*args, )
        self.message = message

    def __str__(self) -> str:
        return f"{self.message}"

class JWTAuthenticationFailedException(APIException):
    """
    JWT认证异常
    """

    def __init__(self, code=201, message=None, args=('异常',)):
        if not message:
            message = 'JWT authentication failed!'
        super().__init__(code, message, args)

def op_exception_handler(ex, context):
    """
    全局异常处理:统一异常拦截处理
    目的:(1)取消所有的500异常响应,统一响应为标准错误返回
        (2)准确显示错误信息
    :param ex:
    :param context:
    :return:
    """
    msg = ''
    code = '201'

    if isinstance(ex, AuthenticationFailed):
        code = 401
        msg = ex.detail
    elif isinstance(ex, DRFAPIException):
        set_rollback()
        msg = ex.detail
    elif isinstance(ex, exceptions.APIException):
        set_rollback()
        msg = ex.detail
    elif isinstance(ex, Exception):
        logger.error(traceback.format_exc())
        msg = str(ex)
    return ErrorResponse(msg=msg, code=code)

四.分页组件 Pagination

0.分页介绍

DRF 框架提供了三个类来实现分页功能

  • PageNumberPagination : 可选分页类, 可以选择查询某一页内容
# 子类中可定义的属性 : 
- page_size              # 每页数目
- page_query_param       # 前端发送的页数关键字名,默认为”page”
- page_size_query_param  # 前端发送的每页数目关键字名,默认为None
- max_page_size          # 前端最多能设置的每页数量
  • LimitOffsetPagination : 偏移分页类, 可以指定从第几条数据开始查询
# 子类中可定义的属性
- default_limit             # 默认限制,默认值与PAGE_SIZE设置一直
- limit_query_param limit   # 参数名,默认’limit’
- offset_query_param offset # 参数名,默认’offset’
- max_limit                 # 最大limit限制,默认None
  • CursorPagination : 游标分页类(加密分页), 只能上一页下一页, 不能指定, 并且url中的页码是加密的
# 子类中可定义的属性
- cursor_query_param  # 默认查询字段
- page_size           # 每页数目
- ordering            # 按什么排序,需要指定

1.可选分页类的使用 : PageNumberPagination

  • 方式一 : 直接使用类实例化来实现
# views.py 视图类
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination

class PageNumberView(APIView):

    def get(self, request, *args, **kwargs):
        # 拿到所有数据
        book_obj = models.Book.objects.all()
        # 使用类实例化得到分页器对象
        page_obj = PageNumberPagination()
        # 设置四个参数
        page_obj.page_size = 2                   # 默认每页显示的条数
        page_obj.max_page_size = 4               # 每页最大显示条数
        page_obj.page_query_param = 'page'       # 查询页数的关键字(?page=2)
        page_obj.page_size_query_param = 'size'  # 每页显示查询条数条件关键字(?page=2&size=2)
        # 调用分页器对象方法对数据进行分页处理
        # 参数: queryset(数据集),request(请求),view(处理分页的视图类)
        page = page_obj.paginate_queryset(queryset=book_obj, request=request, view=PageNumberView)
        # 将分页后的数据对象进行序列化处理
        page_ser = BookModelSerializer(instance=page, many=True)
        # get_next_link() : 下一页
        # get_previous_link() : 上一页
        return return Response({'status': 200, 'msg': '查询成功', 'data': page_ser.data, 'next': page_obj.get_next_link(),'previous': page_obj.get_previous_link()})
        # 或者直接使用分页类自带的Response(自带上一页下一页)
        # return page_obj.get_paginated_response(page_ser.data)

# urls.py 路由
path('book1/', views.PageNumberView.as_view()),
  • 展示效果

img

  • 默认显示 2 条, 最大显示 4 条, 如果你在 url 中输入的 size 超过最大条数, 则只显示最大条数
  • 超过显示的页数, 则显示 "无效页面"
  • 方式二 : 自定义普通(页码)分页类
# custompage.py 新建一个自定义分页类文件
# 写一个类, 继承分页类, 重写类属性
from rest_framework.pagination import PageNumberPagination

class CustomPageNum(PageNumberPagination):
    page_size = 3
    max_page_size = 4
    page_query_param = 'page'
    page_size_query_param = 'size'

# views.py 视图类
# 导入自定义分页类
from drf_test.page.custompage import CustomPageNum

class PageNumberView2(ViewSetMixin,ListAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer
    # 指定使用的分页类
    pagination_class = CustomPageNum

# urls.py 路由
path('book2/', views.PageNumberView2.as_view(actions={'get':'list'})),
  • 展示效果

img](htt

2.偏移分页类的使用 : LimitOffsetPagination

  • 方式一 : 直接使用类实例化来实现
# views.py 视图类
from rest_framework.pagination import LimitOffsetPagination

class LimitOffView(APIView):
    def get(self,request,*args,**kwargs):
        book_obj = models.Book.objects.all()
        # 使用类实例化得到分页器对象
        page_obj = LimitOffsetPagination()
        # 设置四个参数
        page_obj.default_limit = 2              # 默认显示几条
        page_obj.max_limit = 5                  # 最多显示几条
        page_obj.limit_query_param = 'limit'    # 关键字指定取几条(?limit=3)
        page_obj.offset_query_param = 'offset'  # 偏移,从第几个位置后开始取(?offset=5&limit=3)
        # 调用分页器对象方法对数据进行分页处理
        # 参数: queryset(数据集),request(请求),view(处理分页的视图类)
        page = page_obj.paginate_queryset(queryset=book_obj, request=request, view=self)
        # 将分页后的内容进行序列化处理
        page_ser = BookModelSerializer(instance=page,many=True)
        return page_obj.get_paginated_response(page_ser.data)

# urls.py 路由
path('book3/', views.LimitOffView.as_view()),
  • 效果展示

img

  • limit 指定显示几条就显示几条, 超过设置的最大显示条数则只显示最大条数
  • offset 指定从哪个位置后开始显示, 比如指定为 2 , 则从第三条开始显示指定的条数
  • 方式二 : 自定义偏移分页类
# custompage.py 自建分页类
# 写一个类, 继承分页类, 重写类属性
from rest_framework.pagination import LimitOffsetPagination

# 继承LimitOffsetPagination重写
class CustomLimitOff(LimitOffsetPagination):
    default_limit = 2
    max_limit = 5
    limit_query_param = 'limit'
    offset_query_param = 'offset'

# views.py 视图类
# 导入自定义的偏移分页类
from drf_test.page.custompage import CustomLimitOff

class LimitOffView2(ViewSetMixin,ListAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer
    # 指定自定义的偏移分页类
    pagination_class = CustomLimitOff

# urls.py 路由
path('book4/', views.LimitOffView2.as_view(actions={'get':'list'})),
  • 效果演示

img](htt

3.游标(加密)分页类的使用 : CursorPagination

  • 方式一 : 直接使用类实例化来实现
# views.py 视图类
from rest_framework.pagination import CursorPagination

class CursorPageView(APIView):
    def get(self,request,*args,**kwargs):
        book_obj = models.Book.objects.all()
        # 使用类实例化得到分页器对象
        page_obj = CursorPagination()
        # # 设置三个参数
        page_obj.cursor_query_param = 'cursor'  # 查询关键字
        page_obj.page_size = 2      # 每页显示几条
        page_obj.ordering = 'id'    # 按照哪个字段进行排序
        # 调用分页器对象方法对数据进行分页处理
        # 参数: queryset(数据集),request(请求),view(处理分页的视图类)
        page = page_obj.paginate_queryset(queryset=book_obj, request=request, view=self)
        # 将分页后的内容进行序列化处理
        page_ser = BookModelSerializer(instance=page,many=True)
        return page_obj.get_paginated_response(page_ser.data)

# urls.py 路由
path('book5/', views.CursorPageView.as_view()),
  • 效果演示

img

由于该分页类对也没进行了加密, 所以无法直接通过输入页码的方式进行查询(自定义CursorPaginator可以通过链接实现)

  • 方式二 : 自定义游标类实现
# custompage.py 自建分页类
# 写一个类, 继承分页类, 重写类属性
from rest_framework.pagination import CursorPagination

# 1.继承CursorPagination类重写
class CustomCursor(CursorPagination):
    cursor_query_param = 'cursor'
    page_size = 2
    ordering = 'id'

# views.py 视图类
# 导入自定义游标类
from drf_test.page.custompage import CustomCursor

class CursorPageView2(ViewSetMixin, ListAPIView):
    queryset = models.Book.objects.all()
    serializer_class = BookModelSerializer
    # 指定游标类
    pagination_class = CustomCursor

# urls.py 路由
path('book6/', views.CursorPageView2.as_view(actions={'get':'list'})),
  • 效果演示

img

有上面演示结果可以看出 : url 中的页码是经过加密的, 页面提供给你两个链接, 只能进行上一页和下一页, 无法指定某一页跳转

  • 报错问题 : Using cursor pagination, but filter class OrderingFilter returned a None ordering.
# 过滤以及排序与加密分页的冲突,如果setting.py设置了自定义的排序就会出现该问题
# 在setting.py文件中将'DEFAULT_FILTER_BACKENDS'注释

4.全局使用

  • 在 settings.py 文件中进行配置
REST_FRAMEWORK = {
    # 配置要使用的分页类
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    # 配置类属性
    'PAGE_SIZE' : 3
}

5.三种分页类总结

  • CursorPagination也可以被称为加密分页, 会对页码进行加密处理, 访问者无法通过修改页码来进行访问
  • 这种方式相对于PageNumberPagination分页的优点是避免因用户任意修改页码, 从而数据库查询数量过大, 造成数据库过载和查询速度慢的问题
  • 这个也是数据库查询性能优化, 例如PageNumberPagination中用户可以直接将页码改为10000, 数据库需要从头遍历检索到10000这条记录
  • 而CursorPagination中只能查看上下页, 对数据库产生的压力极小, 但对用户的体验不友

6.分页类copy版

'''
分页器
'''
from rest_framework.pagination import PageNumberPagination
from utils.json_response import APIResponse, SuccessResponse
from collections import OrderedDict
from django.utils.translation import gettext_lazy as _

class StandardResultsSetPagination(PageNumberPagination):
    """全局的分页类,所有的list请求会调用"""
    page_size = 10  # 每页显示的条数
    page_query_param = "page"
    page_size_query_param = 'size'  # 前端发送的页数关键字名
    max_page_size = 100  # 每页最大显示的条数
    invalid_page_message = _('无效的页码,当前页数已超过最大值!')

    def get_paginated_response(self, data):
        return APIResponse(OrderedDict([
            ('count', self.page.paginator.count),
            ('next', self.get_next_link()),
            ('previous', self.get_previous_link()),
            ('data', data),
        ]))

def CusPage(data_obj, ser, request, views):
    # 当面分页器不满足使用时,可以使用此分页器
    page_obj = StandardResultsSetPagination()
    # page_obj.page_size = 10  # 默认每页
    page_obj.page_size = 100  # 默认每页
    # 显示的条数
    # page_obj.max_page_size = 20  # 每页最大显示条数
    page_obj.max_page_size = 100  # 每页最大显示条数
    page_obj.page_query_param = 'page'  # 查询页数的关键字(?page=2)
    page_obj.page_size_query_param = 'size'

    page = page_obj.paginate_queryset(queryset=data_obj, request=request, view=views)

    page_ser = ser(instance=page, many=True)
    return page_obj, page_ser
# CusPage视图中使用方法
page_obj, page_ser = CusPage(data_obj=dispatch_obj, ser=OrderDispatchListSerializer, request=request,
                                     views=InventoryView)
return APIResponse({'count': len(dispatch_obj), 'next': page_obj.get_next_link(),
                            'previous': page_obj.get_previous_link(), 'data': page_ser.data})

版权声明:
作者:淘小欣
链接:https://blog.taoxiaoxin.club/180.html
来源:淘小欣的博客
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
海报
Django-rest-framework过滤,排序,分页,异常处理
一.过滤组件 Filter 过滤是对多条数据进行筛选, 所以, 它只针对于 list(获取所有) 在请求路径中携带过滤条件, 对查询的结果进行过滤 1.drf 内置的过滤组件 S……
<<上一篇
下一篇>>