分页、排序、过滤功能、异常处理

分页、排序、过滤功能、异常处理

  • 分页
  • 排序
  • 过滤
  • 异常处理

分页

REST framework提供了分页的支持

添加分页的方式

1.可以在配置文件中设置全局的分页方式

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS':  'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 100  # 每页数目
}

2.可以自定义分页类,为视图添加不同的分页方式,在视图中通过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即可

分页器的类型

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
    

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
    

CursorPagination

  • 前端访问的网址

    使用GET请求  
    http://127.0.0.1/four/students/?cursor=cD0xNQ%3D%3D
    
  • 子类中定义的属性

    cursor_query_param:默认查询字段,不需要修改
    page_size:每页数目
    ordering:按什么排序,需要指定
    

排序

对于列表数据,REST framework提供了OrderingFilter过滤器来帮助我们快速指明数据按照指定字段进行排序

排序的要求

只有查询所有才设计到排序,其他的接口用不到

排序功能必须继承GenericAPIView+ListModelMixin的子视图类上

使用排序的步骤

  • 配置排序类

    filter_backends=[OrderingFilter,]
    
  • 配置排序的字段

    ordering_fields=['id','price']
    
  • 支持前端的访问方式

    http://127.0.0.1:8000/books/?ordering=-price,id
    

    先按价格的降序排,如果价格一样再按id的升序排

排序的规则

排序是升序还是降序都是自己设置的,需要继承了APIView的,从请求地址中取出排序规则,自己排序

'price','-id'=reqeust.query_params.get('ordering').split(',')
qs = Book.objects.all().order_by('price','-id')
  • 注意

    分页和排序能一起用,但是是先排序后分页的

过滤

对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-fitlter扩展来增强支持。

内置过滤

查询所有才涉及到过滤,其它接口都不需要

restful规范中有一条,请求地址中带过滤条件:分页,排序,过滤统称为过滤

内置过滤类使用步骤

必须是继承GenericAPIView+ListModelMixin的子视图类上

  • 配置过滤类

    filter_backends=[SearchFilter,]
    
  • 配置过滤字段

    ordering_fields=['name','publish']
    
  • 支持前端的访问方式

    http://127.0.0.1:8000/books/?search=过滤字段
    

    只要name中或publish中有这个过滤的字段就都能搜出来

  • 注意

    内置过滤类只能通过search写条件,如果配置了多个过滤字段,是或者的条件

精准查询

内置的过滤类 只能通过?search=搜索条件 这种搜索,不能指定 name=三国&publish=出版社

  • 精准查询步骤

    1.需要导入第三方模块django-filter

    pip3 install django-filter
    

    2.配置在视图类中配置

    filter_backends = [DjangoFilterBackend, ]
    

    3.配置搜索的字段

    filterset_fields = ['name', 'publish']
    

    4.搜索

    http://127.0.0.1:8008/books/?name=三国演义&publish=南京出版社
    

    此搜索是精准搜索,条件是并且的关系,必须同时满足才能查询到

自定义过滤类

自定义过滤类步骤

  • 写一个类,继承BaseFilterBackend

  • 重写某个方法:filter_queryset,在该方法中实现过滤,返回qs对象

    def filter_queryset(self, request, queryset, view):
        # 在这里面实现过滤,一定要返回qs对象,过滤后的数据
        name = request.query_params.get('name', None)
        publish = request.query_params.get('publish', None)
        qs = queryset.filter(name__icontains=name, publish__icontains=publish)
        return qs
    
  • 配置在视图类上

    filter_backends = [MyFilter, ]
    

多个过滤类和排序类可以共用,filter_backends=[],可以配置多个,执行顺序是从做往右,所以,放在最左侧的尽量先过滤掉大部分数据

过滤类面试笑技巧

  • 你在工作中遇到的问题及如何解决的?

    我在写xx接口的时候,因为我们过滤条件很多,搜索数据库很慢,写了很多搜索类,之前时候搜索类的排序是随意的,搜索效率比较低,后来,读了drf搜索类的源码,发现执行顺序是从左往右,我就想到了,最左侧的搜索类,尽量搜索速度快,并且过滤掉的数据多,后续再搜索就会更快,所有我调整了一下搜索类的配置顺序,发现该接口的效率有所提升

全局异常处理

REST framework提供了异常处理,我们可以自定义异常处理函数。

内置异常处理

drf中无论在三大认证还是视图类的方法中执行,只要报错(主动抛异常),都能执行一个函数(异常处理的函数)

  • 内置异常处理

    from rest_framework.views import exception_handler
    
    def custom_exception_handler(exc, context):
        # 先调用REST framework默认的异常处理方法获得标准错误响应对象
        response = exception_handler(exc, context)
    
  • 在配置文件中声明自定义的异常处理

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

自定义异常处理

自定义异常处理需要重写exception_handler函数

# 写一个函数
def common_exception_handler(exc, context):
    # 记录日志:只要进入到这里面,一定是出了异常,只要出异常就要记录日志
    request = context.get('request')  # 当此请求的request
    try:
        username = request.user.username
    except:
        username = '没有登录'
    ctime = time.time()
    path = request.path
    method_type = request.method
    print('%s用户,在%s时间,访问%s接口,通过%s请求访问,出了错误:%s' % (username, str(ctime), path, method_type, str(exc)))
    # 后期以上的代码要写到日志中
    
    
    response = exception_handler(exc, context)  # 内置的这个异常处理函数,只处理了drf自己的异常(继承了了APIException的异常)
    if response:  # 如果response有值,说明错误被处理了(Http404,PermissionDenied,APIException)
        return Response({'code': 888, 'msg': 'drf错误,错误原因是:%s' % response.data.get('detail', '未知错误')})
    else:  # 如果是None,这个错误没有被处理,说明这个错误不是drf的错误,而是django的或代码的错误
        return Response({'code': 999, 'msg': '系统错误,错误原因是:%s' % str(exc)})

settings配置文件中

REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'app01.exctptions.common_exception_handler'
}
posted @ 2022-10-10 20:36  Nirvana*  阅读(83)  评论(0)    收藏  举报