DRF-通用分页器(PageNumberPagination):ListModelMixin可以使用的通用分页器

一、ListModelMixin 和GenericAPIView源码

ListModelMixin 是一个单一功能类,必须配合GenericAPIView(或其子类)来一起使用,才能完成其视图的功能

class ListModelMixin:"""List a queryset."""def list(self, request, *args, **kwargs):#1、这里获取querysetqueryset = self.filter_queryset(self.get_queryset())#2、这里获取当前页的queryset数据,[<数据模型>]page = self.paginate_queryset(queryset)if page is not None:#3、对当前页的模型对象,进行序列化serializer = self.get_serializer(page, many=True)#4、获取到当前页的数据,里面整合了当前页的序列化数据return self.get_paginated_response(serializer.data)serializer = self.get_serializer(queryset, many=True)return Response(serializer.data)

主要就按照:一步一步走就可以了,如果ListModelMixin没有的方法,就去GenericAPIView找

self.filter_queryset(self.get_queryset())--->self.paginate_queryset(queryset)-->self.get_serializer(page, many=True)-->self.get_paginated_response(serializer.data)

1、ListModelMixin : queryset = self.filter_queryset(self.get_queryset())

当前类是没有实现get_queryset和filter_queryset方法的,

配合GenericAPIView使用,调用的是GenericAPIView的get_queryset和filter_queryset方法

2、GenericAPIView:  get_queryset, 拿到queryset对象

    def get_queryset(self):queryset = self.querysetif isinstance(queryset, QuerySet):queryset = queryset.all()return queryset

3、GenericAPIView:filter_queryset 这个是过滤器,没有配置就是空,没有做如何操作

4、GenericAPIView:paginate_queryset(queryset) :

4.1、GenericPAIView : paginator : 拿到配置的分页器类,返回分页器的实例化对象

@property
def paginator(self):if not hasattr(self, '_paginator'):if self.pagination_class is None:self._paginator = Noneelse:self._paginator = self.pagination_class()return self._paginator

4.2、根据4.1,拿到的分页器对象,调用分页器的paginate_queryset 方法: 该方法就是返回当前页的queryset

 def paginate_queryset(self, queryset):if self.paginator is None:return Nonereturn self.paginator.paginate_queryset(queryset, self.request, view=self)

5、GenericAPIView: get_paginated_response 方法: 调用分页器对象的get_paginated_response

def get_paginated_response(self, data):assert self.paginator is not Nonereturn self.paginator.get_paginated_response(data)

总结来说:

1、要满足第一个要求,符合ListModelMixin的功能,需要在自定义的分页器类中重写paginate_queryset和get_paginated_response

2、在start方法中,调用这两个方法

3、再开一个方法handle_data,在get_paginated_response中调用这个方法,后续重写handle_data来进行数据定制功能

二、自定义PageNumber的分页器

(1)自定义的基本分页器类

from rest_framework.pagination import PageNumberPaginationclass GenericPageNumberPagination(PageNumberPagination):page_size = 20page_query_param = 'page'page_size_query_param = 'page_size'max_page_size = page_size+(page_size//2)#重写父类的: 校验page_sizedef get_page_size(self, request):'''功能:校验page_size,没有传递或传递类型有问题时,按照默认的大小设置,超过最大页面大小时,设置成最大页面:param request::return: 数值'''page_size = request.GET.get(self.page_size_query_param)if not page_size:page_size = self.page_size #取的是类的page_sizeelse:try:page_size = int(page_size)except Exception:page_size = self.page_size #取的是类的page_sizeif page_size > self.max_page_size:page_size = self.max_page_size#给分页的实例对象赋值每页大小self.page_size = page_sizereturn self.page_size#自己定义: 校验pagedef get_page(self,request,queryset,page_size):'''功能:判断携带page是否合法,页码必须大于0,不能大于最大页数,页码必须是数值:param request: 当前请求对象:param queryset: 模型uqeryset:param page_size:  每页大小:return: None,{"error":'错误消息'},数值'''page = request.GET.get(self.page_query_param)if page == None:return Nonetry:page = int(page)self.page = page#判断查询的页码是否大于0if page <=0:self.page = {'error':'页码必须大于0','code':400}#总数据量self.count = len(queryset)##判断查询的数据是否有数据if self.count == 0:self.page = {'error':'查询不到相关数据','code':400}#总页数self.pages, has_number = divmod(self.count,page_size)if has_number:self.pages += 1## 判断查询的页码是否大于总页码if page > self.pages:self.page = {'error':f'查询的页码{page}大于总页码数{self.pages}','code':400}return self.pageexcept Exception:self.page = {'error':'页码必须是数值','code':400}return self.page#自定义的def has_next_page(self,page=None,pages = None):'''是否有下一页的数据:param page: 当前页:param pages: 总页数:return: 0或1'''if page == None:page = self.pageif pages == None:pages = self.pagesif page == 1:if pages <= 1:return 0else:return 1else:#page > 1if pages > page:return 1else:return 0#自定义的def has_previous_page(self,page=None,pages = None):'''功能:是否有上一页的数据:param page: 当前页:param pages: 总页数:return: 0或1'''if page == None:page = self.pageif pages == None:pages = self.pagesif pages <=1:return 0else:if page>1:return 1else:return 0#重写父类的: 获取当前页的QuerySet对象def paginate_queryset(self, queryset, request, view=None):'''功能:获取当前页的queryset对象:param queryset: 所有的queryset:param request: 当前请求:param view: 当前视图类:return: None,{’error‘:''}, QuerySet'''page_size = self.get_page_size(request)page = self.get_page(request,queryset,page_size)if isinstance(page,dict) or page == None:#page参数错误,没有传递page时return pagequeryset_len = len(queryset)if queryset_len == 0:return {'error':'查询不到相关数据','code':400}#1、总数据量,在get_page 中就设置了self.count = len(queryset)#2、总页数,在get_page 中就设置了self.pages, has_number = divmod(self.count, page_size)if has_number:self.pages += 1# 3、下一页self.next = self.has_next_page()  # 有下一页,返回1,没有返回0# 4、上一页self.previous = self.has_next_page()  # 有上一页时,返回1,没有返回0#5、当前页self.current_page = self.page#6、截取指定的数据if self.page == 1:self.queryset = queryset[:self.page_size]else:start = self.page_size*(self.page-1)end = self.page_size*(self.page)self.queryset = queryset[start:end]return self.queryset#重写父类的:将当前页的QuerySet对象序列化def get_paginated_response(self,page_queryset,serializer_class=None):'''功能:根据传递进来的dict,None(page没有传递,就不返回数据了),当前页的List,当前页QuerySet,:param page_queryset: dict,None,List,QuerySet:param serializer_class: 序列化器类:return: {}'''if isinstance(page_queryset,dict):#paginate_queryset的错误消息return page_querysetelif isinstance(page_queryset,list):data = {'code': 201,'msg': '获取数据成功','data': '',  # 当前页数据'next': self.has_next_page(),  # 是否有下一页'previous': self.has_previous_page(),  # 是否有上一页'count': self.count,  # 总数据量'pages': self.pages,  # 总页码数'current_page': self.page,  # 当前页码'page_size': self.page_size,  # 每页大小}if serializer_class:#1、手动使用start方法调用时,page_ser = serializer_class(instance=page_queryset, many=True)page_data = page_ser.datadata['data'] = page_datareturn dataelse:#2、配合mixins.ListModelMixin源码使用, 其调用了paginate_queryset,拿到queryset对象,将序列化结果传递进来page_queryset = self.handle_data(page_queryset)data['data'] = page_querysetreturn dataelif page_queryset == None:return {'code':400,'error':'没有传递页码值'}else:raise Exception('分页器只支持,dict,list,QuerySet,None 类型')#自己定义的,对当前页数据,进一步的处理def handle_data(self,data_list):return data_list#手动调用时,获取结果的def start(self,request,serializer_class,queryset):#1、获取当前页的QuerySet对象page_queryset = self.paginate_queryset(queryset=queryset,request=request)if page_queryset == None:return {'code':400,'error':'页码必须携带'}if isinstance(page_queryset,dict):return page_queryset#2、将当前页的QuerySet对象传递进去,得到分页的响应结果page_data = self.get_paginated_response(page_queryset=page_queryset,serializer_class=serializer_class)return page_dataif __name__ == '__main__':'''mixins.ListModelMixin的调用逻辑:1、调用paginate_queryset2、1的返回值如果是None时,就不执行获取分页的数据,就去获取所有的数据返回3、1的返回值不为None时,就执行get_paginated_response,拿到分页的结果手动调用使用:data = page.start(request,序列化类,模型对象)'''

(2)、使用

1、对于ListModelMixin,使用的逻辑是一样的,无需变化。

2、手动使用

page = GenericPageNumberPagination()
page_data = page.start(request,'序列化器类','数据库查询的queryset')

(3)、继承

对于需要特殊处理返回的数据时,可以继承分页器类,重写handle_data方法

class UserPageNumberPagination(GenericPageNumberPagination):def handle_data(self,data_list):for dic in data_list:dic['type'] = '患者'return data_list#使用
page = UserPageNumberPagination()
page_data = page.start(request,'序列化器类','数据库查询到关于用户的queryset')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/165264.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

腾讯云点播小程序端上传 SDK

云点播是专门应对上传大视频文件的。 腾讯云点播文档&#xff1a;https://cloud.tencent.com/document/product/266/18177 这个文档比较简单&#xff0c;实在不行&#xff0c;把demo下载下来&#xff0c;一看就明白了&#xff0c;然后再揉一下挪到自己的项目里。完事。 getSign…

芯知识 | 混音播报语音芯片的优势:革新音频应用的新力量

随着科技的进步&#xff0c;语音芯片在各个领域的应用越来越广泛。而在众多语音芯片中&#xff0c;混音播报语音芯片以其独特的优势&#xff0c;正逐渐成为音频应用领域的翘楚。本文将重点探讨混音播报语音芯片的优势及其在现代科技应用中的价值。 一、混音播报语音芯片概述 …

element-vue实现网页锁屏功能

1.写一个锁屏页面&#xff0c;这里比较简单&#xff0c;自己定义一下,需要放到底层HTML中哦&#xff0c;比如index.html <div id"appIndex"><el-dialog title"请输入密码解锁屏幕" :visible.sync"lockScreenFlag" :close-on-click-mod…

力扣236. 二叉树的最近公共祖先(java DFS解法)

Problem: 236. 二叉树的最近公共祖先 文章目录 题目描述思路解题方法复杂度Code 题目描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&am…

Android逆向一-frida操作

系列文章目录 第一章 frida操作 文章目录 系列文章目录前言一、两种模式二、frida命令行执行及参数三、frida使用python执行四、动静态域调用1. 静态域调用2.动态域调用 五. 远程rpc调用六. 补充总结 前言 熟悉frida操作&#xff0c;hook手机app的关键位置进行逆向操作 一、…

芯知识 | Flash可更换声音语音芯片—引领音频IC技术革新的新篇章

随着科技的飞速发展&#xff0c;人们对于电子产品的音频性能要求越来越高。在这种背景下&#xff0c;Flash可更换声音语音芯片应运而生&#xff0c;成为音频技术领域的一颗璀璨明星。本文将详细介绍Flash可更换声音语音芯片的特点、优势以及应用场景&#xff0c;展望其在未来科…

【Docker】从零开始:10.registry搭建私有仓库

【Docker】从零开始&#xff1a;10.registry搭建私有仓库 为什么要使用私有仓库关于Docker Registry基于容器搭建registry私有仓库1.下载镜像2. 启动镜像3.修改系统配置文件4.下载ubuntu镜像&#xff0c;修改名称3.提交镜像4.查看镜像 本地搭建私有仓库(目前编译报错找不到包&a…

【管理运筹学】背诵手册(五)| 动态规划

五、动态规划 基本概念 阶段&#xff08;Stage&#xff09;&#xff1a;将所给问题的过程&#xff0c;按时间或空间特征分解成若干相互联系的阶段&#xff0c;以便按次序去求解每阶段的解&#xff0c;常用字母 k k k 表示。 状态&#xff08;State&#xff09;&#xff1a;…

java实现连接linux(上传文件,执行shell命令等)

1 导入pom <dependency><groupId>com.jcraft</groupId><artifactId>jsch</artifactId><version>0.1.55</version></dependency> 2 编写配置类 package com.budwk.app.atest;import com.budwk.app.common.config.AppExceptio…

计算机网络之网络层

一、概述 主要任务是实现网络互连&#xff0c;进而实现数据包在各网络之间的传输 1.1网络引入的目的 从7层结构上看&#xff0c;网络层下是数据链路层 从4层结构上看&#xff0c;网络层下面是网络接口层 至少我们看到的网络层下面是以太网 以太网解决了什么问题&#xff1f; 答…

【Python 千题 —— 基础篇】删除列表值

题目描述 题目描述 删除列表的指定值。有一个列表 [1, 3, 5, 2, 44, 1, 9, 10, 32] &#xff0c;请使用 for 循环删除该列表中与 [44, 1, 9] 列表相同的值&#xff0c;并输出该列表。 输入描述 无输入。 输出描述 输出操作后的列表。 示例 示例 ① 输出&#xff1a; …

记录:通过day.js获取两个日期相差的时间,并转化为年月日的格式

day.js这个日期库真的是很不错的日期库&#xff0c;足够满足日常的开发需求。 Day.js中文网 (fenxianglu.cn) 需求&#xff1a;获取两个日期相差的时间&#xff0c;转化为年月日的形式&#xff1b;话不多少&#xff0c;直接放代码 import dayjs from "dayjs"; imp…

计算机网络之应用层

一、概述 引入目的&#xff1a; 为了方便用户去使用&#xff1b; 该如何方便用户使用网络呢&#xff0c;即怎样帮助用户使用网络&#xff1f; 1.用户需要知道网络资源所在的位置 2.网络上资源一定是在资源子网的主机上 3.资源子网上的主机&#xff0c;在通信子网中用IP地…

qt-C++笔记之终端Ctrl+C关闭界面和ROS节点

qt-C笔记之终端CtrlC关闭界面和ROS节点 code review! 文章目录 qt-C笔记之终端CtrlC关闭界面和ROS节点1.运行2.main.cpp3.main_window.hpp 1.运行 2.main.cpp 3.main_window.hpp

vue-router 路由权限,路由导航守卫

addRouter() 添加路由 使用场景 列如&#xff1a;菜单权限的分配&#xff08;管理员与用户不一致&#xff09; 根据后台返回 参数 定义isAdmin根据isAdmin 分配 let isAdmin true // 添加路由 可以传参 一级路由名称 来添加二级路由 if (isAdmin) {router.addRoute({path: /…

SpringCloud 微服务全栈体系(十六)

第十一章 分布式搜索引擎 elasticsearch 六、DSL 查询文档 elasticsearch 的查询依然是基于 JSON 风格的 DSL 来实现的。 1. DSL 查询分类 Elasticsearch 提供了基于 JSON 的 DSL&#xff08;Domain Specific Language&#xff09;来定义查询。常见的查询类型包括&#xff1…

P1030 [NOIP2001 普及组] 求先序排列

1.先找根&#xff08;后序最后一个元素&#xff09; 2.以根分中序为两个中序即&#xff1a; (相当于分为两个子树) A中序 对应->A后序 &#xff08;长度对应&#xff09; B中序 对应->B后序 &#xff08;长度对应&#xff09; 递归循坏即可&#xff08;中序长度小…

【数据结构(C语言)】浅谈栈和队列

目录 文章目录 前言 一、栈 1.1 栈的概念及结构 1.2 栈的实现 1.2.1. 支持动态增长的栈的结构 1.2.2 初始化栈 1.2.3 入栈 1.2.4 出栈 1.2.5 获取栈顶元素 1.2.6 获取栈中有效元素个数 1.2.7 检查栈是否为空 1.2.8 销毁栈 二、队列 2.1 队列的概念及结构 2.2 队…

Javaweb之前后台分离开发介绍的详细解析

2.1 前后台分离开发介绍 在之前的课程中&#xff0c;我们介绍过&#xff0c;前端开发有2种方式&#xff1a;前后台混合开发和前后台分离开发。 前后台混合开发&#xff0c;顾名思义就是前台后台代码混在一起开发&#xff0c;如下图所示&#xff1a; 这种开发模式有如下缺点&a…

守护进程的理解

什么是守护进程 daemon False # 是否以守护进程方式运行&#xff0c;True守护&#xff0c;False 非守护 在这段代码中&#xff0c;daemon 变量的值决定了进程是否以守护进程方式运行。如果 daemon 的值为 True&#xff0c;则表示进程将以守护进程方式运行&#xff0c;否则为…