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 相关文章推荐
python实现将英文单词表示的数字转换成阿拉伯数字的方法
Jul 02 Python
python爬虫入门教程--正则表达式完全指南(五)
May 25 Python
python中logging库的使用总结
Oct 18 Python
Python实现两款计算器功能示例
Dec 19 Python
基于Pandas读取csv文件Error的总结
Jun 15 Python
在Python 不同级目录之间模块的调用方法
Jan 19 Python
python Popen 获取输出,等待运行完成示例
Dec 30 Python
Pyorch之numpy与torch之间相互转换方式
Dec 31 Python
Python 3.8 新功能来一波(大部分人都不知道)
Mar 11 Python
改变 Python 中线程执行顺序的方法
Sep 24 Python
pytorch 中forward 的用法与解释说明
Feb 26 Python
如何利用python和DOS获取wifi密码
Mar 31 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
php面向对象程序设计入门教程
2019/06/22 PHP
实现复选框全选/全不选切换
2006/12/23 Javascript
jQuery 浮动广告实现代码
2008/12/25 Javascript
JavaScript高级程序设计 读书笔记之十 本地对象Date日期
2012/02/27 Javascript
基于jQuery实现在线选座之高铁版
2015/08/24 Javascript
手机端 HTML5使用photoswipe.js仿微信朋友圈图片放大效果
2016/08/25 Javascript
jQuery倒计时代码(超简单)
2017/02/27 Javascript
nodejs+websocket实时聊天系统改进版
2017/05/18 NodeJs
Node.js搭建小程序后台服务
2018/01/03 Javascript
Vue + better-scroll 实现移动端字母索引导航功能
2018/05/07 Javascript
微信小程序网络封装(简单高效)
2018/08/06 Javascript
使用vuex解决刷新页面state数据消失的问题记录
2019/05/08 Javascript
富文本编辑器vue2-editor实现全屏功能
2019/05/26 Javascript
vue通信方式EventBus的实现代码详解
2019/06/10 Javascript
Vue v-text指令简单使用方法示例
2019/09/19 Javascript
JavaScript实现单图片上传并预览功能
2019/09/30 Javascript
使用Typescript和ES模块发布Node模块的方法
2020/05/25 Javascript
Vue是怎么渲染template内的标签内容的
2020/06/05 Javascript
基于javascript处理二进制图片流过程详解
2020/06/08 Javascript
Python简单调用MySQL存储过程并获得返回值的方法
2015/07/20 Python
Python按行读取文件的简单实现方法
2016/06/22 Python
python学习笔记之列表(list)与元组(tuple)详解
2017/11/23 Python
Python文本处理之按行处理大文件的方法
2018/04/09 Python
Python实现App自动签到领取积分功能
2018/09/29 Python
Python3+Appium安装使用教程
2019/07/05 Python
JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗?
2013/07/02 面试题
会计师事务所审计实习自我鉴定
2013/09/20 职场文书
毕业生的自我鉴定
2013/10/29 职场文书
制冷与电控专业应届生求职信
2013/11/11 职场文书
全国道德模范事迹
2014/02/01 职场文书
幼儿园毕业家长感言
2014/02/10 职场文书
村委会贫困证明范本
2014/09/17 职场文书
收款委托书
2014/10/14 职场文书
Python利用folium实现地图可视化
2021/05/23 Python
Mysql效率优化定位较低sql的两种方式
2021/05/26 MySQL
JavaScript 原型与原型链详情
2021/11/02 Javascript