Django REST Framework 分页(Pagination)详解


Posted in Python onNovember 30, 2020

在前面的DRF系列教程中,我们以博客为例介绍了序列化器, 使用基于类的视图APIView和ModelViewSet开发了针对文章资源进行增删查改的完整API端点,并详细对权限和认证(含jwt认证)进行了总结与演示。在本篇文章中我们将向你演示如何在Django REST Framework中使用分页。

分页

为什么要分页? 当你的数据库数据量非常大时,如果一次将这些数据查询出来, 必然加大了服务器内存的负载,降低了系统的运行速度。一种更好的方式是将数据分段展示给用户。如果用户在展示的分段数据中没有找到自己的内容,可以通过指定页码或翻页的方式查看更多数据,直到找到自己想要的内容为止。

Django REST Framework提供3种分页类,接下来我们会分别进行演示。

  1. PageNumberPagination类:简单分页器。支持用户按?page=3这种方式查询,你可以通过page_size这个参数手动指定每页展示给用户数据的数量。它还支持用户按?page=3&size=10这种更灵活的方式进行查询,这样用户不仅可以选择页码,还可以选择每页展示数据的数量。对于第二种情况,你通常还需要设置max_page_size这个参数限制每页展示数据的最大数量,以防止用户进行恶意查询(比如size=10000), 这样一页展示1万条数据将使分页变得没有意义。
  2. LimitOffsetPagination类:偏移分页器。支持用户按?limit=20&offset=100这种方式进行查询。offset是查询数据的起始点,limit是每页展示数据的最大条数,类似于page_size。当你使用这个类时,你通常还需要设置max_limit这个参数来限制展示给用户数据的最大数量。
  3. CursorPagination类:加密分页器。这是DRF提供的加密分页查询,仅支持用户按响应提供的上一页和下一页链接进行分页查询,每页的页码都是加密的。使用这种方式进行分页需要你的模型有"created"这个字段,否则你要手动指定ordering排序才能进行使用。

使用PageNumberPagination类

DRF中使用默认分页类的最简单方式就是在settings.py中进行全局配置,如下所示:

REST_FRAMEWORK ={
 'DEFAULT_PAGINATION_CLASS':'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE':2
}

展示效果如下,每页展示两条记录, 不支持用户指定每页展示数据的数量。

Django REST Framework 分页(Pagination)详解

但是如果你希望用户按?page=3&size=10这种更灵活的方式进行查询,你就要进行个性化定制。在实际开发过程中,定制比使用默认的分页类更常见,具体做法如下。

第一步: 在app目录下新建pagination.py, 添加如下代码:

#blog/pagination.py

from rest_framework.pagination import PageNumberPagination
 
 
class MyPageNumberPagination(PageNumberPagination):
 page_size = 2 # default page size
 page_size_query_param = 'size' # ?page=xx&size=??
 max_page_size = 10 # max page size

我们自定义了一个MyPageNumberPagination类,该类继承了PageNumberPagination类。我们通过page_size设置了每页默认展示数据的条数,通过page_size_query_param设置了每页size的参数名以及通过max_page_size设置了每个可以展示的最大数据条数。

第二步:使用自定义的分页类

在基于类的视图中,你可以使用pagination_class这个属性使用自定义的分页类,如下所示:

from rest_framework import viewsets
from .pagination import MyPageNumberPagination
 
 
 
 
class ArticleViewSet(viewsets.ModelViewSet):
 # 用一个视图集替代ArticleList和ArticleDetail两个视图
 queryset = Article.objects.all()
 serializer_class = ArticleSerializer
 pagination_class = MyPageNumberPagination
 
 
 
 
 # 自行添加,将request.user与author绑定
 def perform_create(self, serializer):
  serializer.save(author=self.request.user)
 
 
 # 自行添加,将request.user与author绑定
 def perform_update(self, serializer):
  serializer.save(author=self.request.user)

展示效果如下所示:

Django REST Framework 分页(Pagination)详解

当然定制分页类不限于指定page_size和max_page_size这些属性,你还可以改变响应数据的输出格式。比如我们这里希望把next和previous放在一个名为links的key里,我们可以修改MyPageNumberPagination类,重写get_paginated_response方法:

from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
 
 
 
 
class MyPageNumberPagination(PageNumberPagination):
 page_size = 2 # default page size
 page_size_query_param = 'size' # ?page=xx&size=??
 max_page_size = 10 # max page size
 def get_paginated_response(self, data):
  return Response({
   'links': {
    'next': self.get_next_link(),
    'previous': self.get_previous_link()
   },
   'count': self.page.paginator.count,
   'results': data
  })

新的展示效果如下所示:

Django REST Framework 分页(Pagination)详解

注意:重写get_paginated_response方法非常有用,你还可以给分页响应数据传递额外的内容,比如code状态码等等。

前面的例子中我们只在单个基于类的视图或视图集中使用到了分页类,你还可以修改settings.py全局使用你自定义的分页类,如下所示。展示效果是一样的,我们就不详细演示了。

REST_FRAMEWORK = {
 'DEFAULT_PAGINATION_CLASS': 'blog.pagination.MyPageNumberPagination',
}

使用LimitOffsetPagination类

使用这个分页类最简单的方式就是在settings.py中进行全局配置,如下所示:

REST_FRAMEWORK = {
 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination'
}

展示效果如下所示,从第6条数据查起,每页展示2条。

Django REST Framework 分页(Pagination)详解

你也可以自定义MyLimitOffsetPagination类,在单个视图或视图集中使用,或者全局使用。

from rest_framework.pagination import LimitOffsetPagination
 
 
class MyLimitOffsetPagination(LimitOffsetPagination):
 default_limit = 5 # default limit per age
 limit_query_param = 'limit' # default is limit
 offset_query_param = 'offset' # default param is offset
 max_limit = 10 # max limit per age

使用CursorPagination类

全局使用

REST_FRAMEWORK = {
 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.CursorPagination',
 'PAGE_SIZE': 2
}

展示效果如下所示:

Django REST Framework 分页(Pagination)详解

什么? 为什么会出错误? 使用CursorPagination类需要你的模型里有created这个字段,否则你需要手动指定ordering字段。这是因为CursorPagination类只能对排过序的查询集进行分页展示。我们的Article模型只有create_date字段,没有created这个字段,所以会报错。

为了解决这个问题,我们需要自定义一个MyCursorPagination类,手动指定按create_date排序, 如下所示:

#blog/pagination.py

from rest_framework.pagination import CursorPagination
 
 
class MyArticleCursorPagination(CursorPagination):
 page_size = 3 # Default number of records per age
 page_size_query_param = 'page_size' 
 cursor_query_param = 'cursor' # Default is cursor
 ordering = '-create_date'

修改settings.py, 使用自己定义的分页类。

REST_FRAMEWORK = {
 'DEFAULT_PAGINATION_CLASS': 'blog.pagination.MyArticleCursorPagination',
}

响应效果如下所示,你将得到previous和next分页链接。页码都加密了, 链接里不再显示页码号码。默认每页展示3条记录, 如果使用?page_size=2进行查询,每页你将得到两条记录。

Django REST Framework 分页(Pagination)详解

当然由于这个ordering字段与模型相关,我们并不推荐全局使用自定义的CursorPagination类,更好的方式是在GenericsAPIView或视图集viewsets中通过pagination_class属性指定,如下所示:

from rest_framework import viewsets
from .pagination import MyArticleCursorPagination
 
 
 
 
class ArticleViewSet(viewsets.ModelViewSet):
 # 用一个视图集替代ArticleList和ArticleDetail两个视图
 queryset = Article.objects.all()
 serializer_class = ArticleSerializer
 pagination_class = MyArticleCursorPagination
 # 自行添加,将request.user与author绑定
 def perform_create(self, serializer):
  serializer.save(author=self.request.user)
 
 
 # 自行添加,将request.user与author绑定
 def perform_update(self, serializer):
  serializer.save(author=self.request.user)

函数类视图中使用分页类

注意pagination_class属性仅支持在genericsAPIView和视图集viewset中配置使用。如果你使用函数或简单的APIView开发API视图,那么你需要对你的数据进行手动分页,一个具体使用例子如下所示:

from rest_framework.pagination import PageNumberPagination
class ArticleList0(APIView):
 """
 List all articles, or create a new article.
 """
 def get(self, request, format=None):
  articles = Article.objects.all()
 
 
  page = PageNumberPagination() # 产生一个分页器对象
  page.page_size = 3 # 默认每页显示的多少条记录
  page.page_query_param = 'page' # 默认查询参数名为 page
  page.page_size_query_param = 'size' # 前台控制每页显示的最大条数
  page.max_page_size = 10 # 后台控制显示的最大记录条数,防止用户输入的查询条数过大
  ret = page.paginate_queryset(articles, request)
  serializer = ArticleSerializer(ret, many=True)
 
 
  return Response(serializer.data)

小结

本文总结了DRF提供的3种分页类并详细演示了如何使用它们,你学会了吗?

到此这篇关于Django REST Framework 分页(Pagination)详解的文章就介绍到这了,更多相关Django REST Framework 分页内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python3使用requests包抓取并保存网页源码的方法
Mar 15 Python
python采用django框架实现支付宝即时到帐接口
May 17 Python
Python 快速实现CLI 应用程序的脚手架
Dec 05 Python
Python+selenium 获取浏览器窗口坐标、句柄的方法
Oct 14 Python
PyCharm代码回滚,恢复历史版本的解决方法
Oct 22 Python
python如何实现数据的线性拟合
Jul 19 Python
Python3 chardet模块查看编码格式的例子
Aug 14 Python
Python要求O(n)复杂度求无序列表中第K的大元素实例
Apr 02 Python
pycharm解决关闭flask后依旧可以访问服务的问题
Apr 03 Python
python 最简单的实现适配器设计模式的示例
Jun 30 Python
利用python对mysql表做全局模糊搜索并分页实例
Jul 12 Python
Python库安装速度过慢解决方案
Jul 14 Python
python代码实现猜拳小游戏
Nov 30 #Python
Django 权限管理(permissions)与用户组(group)详解
Nov 30 #Python
python 如何引入协程和原理分析
Nov 30 #Python
Django缓存Cache使用详解
Nov 30 #Python
Django框架实现在线考试系统的示例代码
Nov 30 #Python
python爬虫 requests-html的使用
Nov 30 #Python
python实现登录与注册系统
Nov 30 #Python
You might like
咖啡风味 世界咖啡主要分布分布 咖啡的生长要求
2021/03/06 新手入门
PHP互换两个变量值的方法(不用第三变量)
2016/11/14 PHP
PHP封装的PDO数据库操作类实例
2017/06/21 PHP
php策略模式简单示例分析【区别于工厂模式】
2019/09/25 PHP
深入理解JavaScript中的传值与传引用
2013/12/09 Javascript
网站内容禁止复制和粘贴、另存为的js代码
2014/02/26 Javascript
jQuery插件开发详细教程
2014/06/06 Javascript
node.js中的console.log方法使用说明
2014/12/09 Javascript
JavaScript中的值是按值传递还是按引用传递问题探讨
2015/01/30 Javascript
理解Javascript的动态语言特性
2015/06/17 Javascript
jQuery实现简单的网页换肤效果示例
2016/09/18 Javascript
Query常用DIV操作获取和设置长度宽度的实现方法
2016/09/19 Javascript
关于js二维数组和多维数组的定义声明(详解)
2016/10/02 Javascript
JS正则表达式学习之贪婪和非贪婪模式实例总结
2016/12/26 Javascript
JavaScript表单验证的两种实现方法
2017/02/11 Javascript
Angular.Js中过滤器filter与自定义过滤器filter实例详解
2017/05/08 Javascript
为vue-router懒加载时下载js的过程中添加loading提示避免无响应问题
2018/04/03 Javascript
详解处理bootstrap4不支持远程静态框问题
2018/07/20 Javascript
Python中尝试多线程编程的一个简明例子
2015/04/07 Python
Python 脚本实现淘宝准点秒杀功能
2019/11/13 Python
Python selenium 自动化脚本打包成一个exe文件(推荐)
2020/01/14 Python
django 将自带的数据库sqlite3改成mysql实例
2020/07/09 Python
从python读取sql的实例方法
2020/07/21 Python
python实现一个简单RPC框架的示例
2020/10/28 Python
Python解析m3u8拼接下载mp4视频文件的示例代码
2021/03/03 Python
Css3实现无缝滚动防抖
2020/09/14 HTML / CSS
Canvas引入跨域的图片导致toDataURL()报错的问题的解决
2018/09/19 HTML / CSS
PHP面试题及答案二
2015/05/23 面试题
机械专业应届生求职信
2013/12/12 职场文书
销售工作岗位职责
2013/12/24 职场文书
流动人口婚育证明范本
2014/09/26 职场文书
2014企业年终工作总结
2014/12/23 职场文书
软件项目经理岗位职责
2015/04/01 职场文书
立秋之描写立秋的作文(五年级)
2019/08/08 职场文书
两行代码解决Jupyter Notebook中文不能显示的问题
2021/04/24 Python
微前端qiankun改造日渐庞大的项目教程
2022/06/21 Javascript