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)

四、继承APIView实现分页

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 配置在配置文件上

 

 

posted @   凡人半睁眼  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示