47.DRF实现分页

时间:2022-12-29 19:06:08

分页Pagination

  当我们在PC 或者 App 有大量数据需要展示时,可以对数据进行分页展示。这时就用到了分页功能,分页使得数据更好的展示给用户
  比如我们有1W+数据要返回给前端,数据量大一次性返回可能会比较慢,前端一次性展示1W+数据也会比较慢,用分页返回数据效果较好

前端分页和后端分页的区别

前端分页
前端分页是一次性把数据全部拿出来进行分页,比如500条数据,一次展示50条,点击下一页再展示下50条,依次类推
 
优缺点:
前端分页一次性把数据加载出来,翻页过程中不会再对后端发起请求,对服务器压力较小
但是当数据量过大的时候,比较耗费性能,加载速度会比较慢
 
后端分页
后端分页是根据前端的需求返回对应的数据给客户端,例如一共有500条数据,一次展示50条,前端传参请求一次只返回50条数据
传下一页的page再请求下一页的数据再返回50条
 
优缺点:
后端分页比较灵活,每次都单独请求数据,对前端性能要求不高, 更易保证数据准确性
因为每次翻页都需要请求后端拿到对应的数据,多用户同时请求会增加后端压力
 
如何选择分页

数据量比较小的时候建议一次性返回数据在前端进行分页,数据量庞大的话建议服务器分页单次请求单次返回

 

DRF中的分页

DRF中的分页介绍
在drf框架中允许自定制分页样式,可以设置每页显示的数量,并且支持以下操作
  • 将分页链接作为响应内容的一部分
  • 响应头中包含分页链接,比如Content-Range或Link
 
drf中使用通用视图或者视图集的时候会自动执行分页,如果使用的常规的APIView等,需要自己调用分页API
  • 可以通过将分页类设置None来选择是否关闭分页功能
 
自有分页VIew源码示例
我们拿ListAPIView举例,ListAPIView继承mixins.ListModelMixin和GenericAPIView,下述代码可以看出源码本身包含了分页功能,如果使用常规VIew则需要自己实现对应逻辑
# ListModelMixin源码 如果是常规view要实现分页,在视图中实现下述代码即可
class ListModelMixin:
 
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        # self.paginate_queryset对原有的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)
 
 
分页的settings设置
DEFAULT_PAGINATION_CLASS和PAGE_SIZE必须同时设置,如果不设置或者只设置一个,就相当于是None不分页
REST_FRAMEWORK = {
    
    # drf的分页类位于rest_framework.pagination中
    "DEFAULT_PAGINATION_CLASS":"",# 全局默认的指定分页类,如果视图想单独指定,与权限一样在view中单独设置
    "PAGE_SIZE": #每一页显示多少数据
    }
 

DRF中的分页类使用详解

BasePagination:
分页的基类,与权限、限流等原理一样,写好模版待后续具体逻辑继承,略过
47.DRF实现分页
PageNumberPagination:
继承BasePagination,这种分页接收前端请求的页码参数,参数是第几页则请求第几页的数据,例如前端请求page=10,则返回第10页数据
# settings.py
REST_FRAMEWORK = {
    # 指定分页类为PageNumberPagination
    "DEFAULT_PAGINATION_CLASS":"rest_framework.pagination.PageNumberPagination",
    "PAGE_SIZE":3 #每一页显示3条数据
    }
如果是基于通用视图或者视图集,当前已经实现了分页功能
  • 图1是没有设置分页时候的请求,返回全部6条数据
  • 图2设置了分页后,默认不传page参数的请默认返回了第一页的数据
  • 图3请求地址后加了page参数,page=2,则返回的是第二页的数据
  • 也可以看出设置分页后response多了三个参数count是接口一共的数量,next是下一页的地址,previous是上一页地址
47.DRF实现分页
PageNumberPagination自定义分页类
上述方式每页请求的数据量是根据settings设置PAGE_SIZE写死的数量进行返回,如果我们想要根据传参定义第几页返回多少条数据,可以自定义分页类
from rest_framework import pagination


# 继承分页类
class PublicPagination(pagination.PageNumberPagination):
    page_size = 2  # 每页显示的默认数据个数
    page_query_param = 'page'  # 页号,第几页的参数 ,比如定义为pages,那么请求分页的参数就应该是pages
    page_size_query_param = 'page_size'  # 自己指定每页显示多少个数
    max_page_size = 100  # 最大允许设置的每页显示的数量
    
    # last_page_strings用于指定表示请求最后一页的参数
    # page=last的时候会直接到最后一页
    # 如果不改参数的话,可以不用设置,不设置的话默认参数就是last
    last_page_strings = 'last' 
''' 
自定义分页类
通过page_size指定默认的每页数据量,
page_size_query_param指定每页自定义的数据量的参数,如果请求page_size=4,则每页显示4个,否则走默认的2
max_page_size是允许设置的每页最大的数据量
'''
# 导入自定义的分页类
from .pagination import PublicPagination

class CategoryViewSet(ModelViewSet):
    queryset = Category.objects.all()
    serializer_class = CategorySerializer
    # 指定自定义的分页类,与权限、限量等不同,每个视图只允许指定一个分页类
    # 对指定视图设置分页类,会覆盖settings中默认的全局配置
    pagination_class = PublicPagination
上述代码实现对应功能后,继续请求接口
  • 图1page=2,请求第二页的数据,返回2条,因为自定义分页类中的page_size覆盖了全局的page_size=3
  • 图2没有指定page参数,默认返回第一页的两条数据
  • 图3指定page=2,page_size=1,返回第二页的数据,用page_size指定1覆盖代码中设置的2,所以只显示1条数据
47.DRF实现分页
 
LimitOwsetPagination:
这个分页类就类似于查找数据库中查找的语法
比如数据库中 Select * from table limit 100,300,从第101条开始,取300的数据
在分页中limit用于指定取多少条数据,offset用于指定从多少条开始,与sql一样,offse+1开始
#view
class CategoryViewSet(ModelViewSet):
    queryset = Category.objects.all()
    serializer_class = CategorySerializer
    # 给该指定LimitOffsetPagination分页类,也可以在settings指定全局
    pagination_class = LimitOffsetPagination
下面使用LimitOwsetPagination分页类请求接口
  • 图1,没有传参返回了3条数据是因为在settings中page_size是3,默认从第一条开始取3条数据,下一页依次向后取3条
  • 图2是将settings中的page_size删除掉,没有传递limit和offset参数就默认全部返回
  • 图3是传入limit=2,offset=2,从第三条开始取2条数据
47.DRF实现分页
LimitOwsetPagination自定义分页类
# 继承LimitOffsetPagination分页类
class PublicLimitOffsetPagination(pagination.LimitOffsetPagination):
    default_limit = 2  # 用于指定默认的limit数量
    limit_query_param = 'lm' # 指定请求时候对应的limit参数名,如果是lm那么传参就是lm=
    offset_query_param = 'of' ## 指定请求时候对应的offset参数名,如果是lm那么传参就是of=
    max_limit = 4 # 最大的limit可设置数量

CursorPagination:

基于光标的分页
CursorPagination分页类说明
  • 显示一个正向和反向的控件,不允许我们任意导航到任意位置
  • 要求结果集中有应该唯一的不变的排序方式
  • 可以确保客户端在翻页时不会看到同一对象两次,即使在分页的同时有数据插入
  • 对于超级大的数据量,使用前两个分页可能会效率低下,基于光标的分页具有固定的时间属性,不会因为数据变大而减慢
  • 基于光标的分页的排序方式默认是使用created排序,如果使用默认排序则模型必须有created时间戳字段,首先显示最近添加的数据
  • 可以覆盖pagination类的ordering属性,或者使用OrderingFilter过滤器类和CursorPagination来修改排序
  • 使用时要注意应该有一个唯一不变的值,例如默认的created
#settings设置 
REST_FRAMEWORK = {
    # 指定CursorPagination分页类
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.CursorPagination",
    "PAGE_SIZE": 2  # 每一页显示3条数据
    }
 
47.DRF实现分页

通过光标进行的分页,可以看出next后面的参数进行了加密,无法直接通过传递参数进行跳转,只能一页一页点击
自定义CursorPagination
#继承CursorPagination分页类
class PublicCursorPagination(pagination.CursorPagination):
    ordering = '-created'  #通过什么进行排序,默认created
    page_size = 3 # 每页数据量
    cursor_query_param = 'cs' #请求的参数字段,默认cursor
47.DRF实现分页