drf 分页类、异常类、接口文档
一、基本分页
1、写一个分页类,继承 PageNumberPagination
web 用这个多
http://api.example.org/accounts/?page=4
http://api.example.org/accounts/?page=4&page_size=100
1 2 3 4 5 6 7 8 | from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination class CommonPageNumberPagination(PageNumberPagination): # 重写几个类属性 page_size = 3 # 每页显示多少条 page_query_param = 'page' # 指定第几页的key值 http://127.0.0.1:8000/books/?page=3 page_size_query_param = 'size' # 可以指定每页显示多少条 size=300000 max_page_size = 5 # 每页最多显示5条<br><br><br> |
2、views
引入自定义的分页类
1 2 3 4 5 6 | from .page import CommonPageNumberPagination class BookView(ViewSetMixin, ListAPIView): queryset = Book.objects. all () serializer_class = BookSerializer pagination_class = CommonPageNumberPagination |
3、效果
二、偏移分页
1、LimitOffsetPagination 使用场景
http://api.example.org/accounts/?limit=4 # 从开始取4条
http://api.example.org/accounts/?offset=4&limit=5 从第4条开始,取5条
1 2 3 4 5 6 7 8 9 | from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination class CommonLimitOffsetPagination(LimitOffsetPagination): # http://api.example.org/accounts/?limit=4 # 从开始取4条 # http://api.example.org/accounts/?offset=4&limit=5 从第4条开始,取5条 default_limit = 2 # 默认每页显示2条 limit_query_param = 'limit' # 每页显示多少条的查询条件 offset_query_param = 'offset' # 从第几条开始取数据 max_limit = 5 # limit最多取5条 |
2、views 引入
1 2 3 4 5 6 | from .page import CommonPageNumberPagination,CommonLimitOffsetPagination,CommonCursorPagination class BookView(ViewSetMixin, ListAPIView): queryset = Book.objects. all () serializer_class = BookSerializer pagination_class = CommonLimitOffsetPagination |
3、效果
三、游标分页
1、CommonCursorPagination
app上用这个多
只能上一页和下一页,不能指定跳转到中间的某页---》效率高,大数据量用它、
1 2 3 4 5 6 7 8 | from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination class CommonCursorPagination(CursorPagination): # app上用这个多 # 只能上一页和下一页,不能指定跳转到中间的某页---》效率高,大数据量用它、 cursor_query_param = 'cursor' # 查询条件,用不到,需要有 page_size = 2 # 每页显示两条 ordering = 'id' # 按id排序 这个必须是表中字段 |
2、引入略过
3、效果
补充:视图类中配置了分页类,为什么就有分页了?
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 | - pagination_class = CommonCursorPagination # GenericAPIView类属性 - pagination_class是GenericAPIView的类属性:继承APIVIew的视图类,是不能这样配置的 - - - - 》自己分页 - 还需要继承:ListModelMixin - - - 》它的 list 方法中,在对所有数据做了分页 - - 》 def list ( self , request, * args, * * kwargs): # 做过滤 queryset = self .filter_queryset( self .get_queryset()) #### 分页开始 page = self .paginate_queryset(queryset) if page is not None : serializer = self .get_serializer(page, many = True ) return self .get_paginated_response(serializer.data) #### 分页结束### serializer = self .get_serializer(queryset, many = True ) return Response(serializer.data) - self .paginate_queryset(queryset)是 GenericAPIView 类的方法 - GenericAPIView的 paginate_queryset def paginate_queryset( self , queryset): if self .paginator is None : return None # 不做分页了 # 做分页 return self .paginator.paginate_queryset(queryset, self .request, view = self ) - self .paginator 方法包装成了属性,就是咱们分页类的对象 CommonCursorPagination() @property def paginator( self ): self ._paginator = self .pagination_class() # 咱们配置的分页类 CommonCursorPagination return self ._paginator - 调用了 分页类对象的paginate_queryset - - - 》完成真正的分页 分页类对象.paginate_queryset(queryset, self .request, view = self ) |
四、继承
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 57 58 | from rest_framework.mixins import ListModelMixin from rest_framework.generics import GenericAPIView from .serializer import BookSerializer from rest_framework.views import APIView from rest_framework.response import Response from .page import MyPageNumberPagination class BookView(APIView): def get( self , request): ordering = request.query_params.get( 'ordering' ) name = request.query_params.get( 'name' ) book_list = Book.objects. all () if ordering: book_list = book_list.order_by(ordering) if name: book_list = book_list. filter (name__contains = name) # 加入分页 # page=MyPageNumberPagination().paginate_queryset(book_list, request,self) # 得到分页对象 pagination = MyPageNumberPagination() # 分页对象调用它的paginate_queryset方法 page = pagination.paginate_queryset(book_list, request, self ) # 把page序列化 ser = BookSerializer(instance = page, many = True ) # 分页类对象调用分页父类中的get_paginated_response方法 return pagination.get_paginated_response(ser.data) # 模仿get_paginated_response中的返回值Response return Response({ 'code' : 100 , 'msg' : '成功' , 'count' : pagination.page.paginator.count, 'next' : pagination.get_next_link(), 'previous' : pagination.get_previous_link(), 'results' : ser.data}) ''' 根据源码写自己的分页 # 配置的分页类的对象调用了paginate_queryset(queryset, self.request, view=self) page = self.paginate_queryset(queryset) # 是paginate_queryset的GenericAPIView ##### 重写了这句话 if page is not None: # 把page进行了序列化 serializer = self.get_serializer(page, many=True) # 返回格式 return self.get_paginated_response(serializer.data) # GenericAPIView--》self.paginator.get_paginated_response(data) PageNumberPagination类的get_paginated_response方法 def get_paginated_response(self, data): return Response(OrderedDict([ ('count', self.page.paginator.count), ('next', self.get_next_link()), ('previous', self.get_previous_link()), ('results', data) ])) ''' |
五、 异常类
1、写一个类文件
1 2 3 4 5 6 7 8 9 | from rest_framework.views import exception_handler from rest_framework.response import Response def common_execption(exc, context): res = exception_handler(exc, context) if not res: # 有值,说明drf异常,它处理完了,没有值,是其他异常,我们自己处理 return Response({ 'code' : 999 , 'msg' : '非drf错误信息是:%s' % str (exc)}) else : return Response({ 'code' : 888 , 'msg' : 'drf错误信息,错误信息是:' + res.data.get( 'detail' )}) |
2、在全局配置文件中引入
1 2 3 | REST_FRAMEWORK = { 'EXCEPTION_HANDLER' : 'app01.exceptions.common_execption' # 异常 } |
3、views中故意引发错误
1 2 3 4 5 6 7 8 9 10 | class BookView(ViewSetMixin, APIView): def list ( self , request, * args, * * kwargs): book_list = Book.objects. all () # raise APIException('这里是主动异常了') prion(asdas) pagination = CommonPageNumberPagination() # 实例化一个对象 page = pagination.paginate_queryset(book_list, request) ser = BookSerializer(instance = page, many = True ) return pagination.get_paginated_response(ser.data) # return Response(ser.data) |
4、效果
补充:
1 为什么在配置文件中配置了自己写的全局异常处理函数,只要出了异常,它就会走?
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 | # 之前是先执行了res = exception_handler(exc, context),现在要自己做,处理drf异常 # 为什么在配置文件中配置了自己写的全局异常处理函数,只要出了异常,它就会走? - APIView执行流程 - - - 》dispatch的 try : except Exception as exc: response = self .handle_exception(exc) #看这里,exc是错误对象 - 执行: self .handle_exception(exc) - - - 》 self 是视图类的对象 - APIView的handle_exception(exc) - handle_exception源码 def handle_exception( self , exc): exception_handler = self .get_exception_handler() # 猜 拿到的就是配置文件中配的那个函数 response = exception_handler(exc, context) #执行这个函数,传了俩参数 return response - self .get_exception_handler()是如何从配置文件中拿出来的 - self .settings.EXCEPTION_HANDLER # 先从项目配置文件中找:key为它的EXCEPTION_HANDLER,如果项目配置文件没有,拿drf内置的配置文件 # 如果你写了全局异常处理函数,配置好了,但是前端还没有返回固定格式,可能的原因是什么? - 1 这个错误不是在三大认证和视图类的方法中产生的,之前产生的 - - 》中间件,包装新的request - 2 你写的common_exception执行出错了 |
2 前后端分离了,后端出了异常,我们不想让前端看到,我们需要捕获全局异常,统一返回格式
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 | # drf 源码中已经处理 APIView - - - >dispatch - - - > try : # 1 执行三大认证 # 2 执行视图类的方法 except Exception as exc: response = self .handle_exception(exc) - 463 行左右: # exception_handler就是配置文件中配置的一个函数-->默认的 # 后期自己写了 exception_handler = self .get_exception_handler() response = exception_handler(exc, context) # 默认执行的是:rest_framework.views.exception_handler函数---》只处理了drf的异常 # 咱们处理全局异常的步骤: 1 写一个函数,在内部处理 from rest_framework.views import exception_handler def common_exception_handler(exc,context): res = exception_handler(exc,context) if res: #这次异常是drf的,并且它处理了 # 我们要统一返回格式 return Response({ 'code' : 888 , 'msg' : "系统异常(drf异常),请联系系统管理员:%s" % res.data.get( 'detail' , None )}) else : return Response({ 'code' : 999 , 'msg' : "系统异常(非drf异常),请联系系统管理员:%s" % str (exc)}) 2 配置在配置文件上 |