一、背景
当用户请求数据时,如果查询出数据结果较多,这时不应该将所有数据一次性返回给用户展示在同一个页面上,可以使用分页功能将数据分批次返回给用户,即可以减轻服务器每次查询所有数据的压力,也可以提升前端展示效果。
分页功能可以自己实现,也可以使用Django内置的分页器。
一、自己实现
将分页功能单独封装成类,再在views中调用
新建utils目录中的paginate.py用于封装分页类
from django.utils.safestring import mark_safe class Paginate: def __init__( self , obj_all, page_id, obj_max = 10 , page_max = 10 ): self .obj_total = obj_all.count() # 分页 每页显示的条目数,默认最多显示10条记录 self .obj_max = obj_max # 每页最多显示的页码个数 self .page_max = page_max try : # 如果获取不到page参数,将page_id默认设置成1 self .page_id = page_id except Exception as e: self .page_id = 1 # 总页码数为page_num self .page_total, more = divmod ( self .obj_total, obj_max) if more: # 如果有余数,则页码数加1 self .page_total + = 1 # 第1页切片,[0:10],取id为0-9的10条记录 # 第2页切片,[10:20],取id为10-19的10条记录 # 第n页切片,[(n-1)*10:n*10] #通过@property将方法封装成属性,在调用实例化对象.方法时就不需要加括号执行 @property def obj_start( self ): obj_start = ( self .page_id - 1 ) * self .obj_max return obj_start @property def obj_end( self ): obj_end = self .page_id * self .obj_max return obj_end @property def obj_id_start( self ): # 第1页序号从1-10,第2页序号从11-20,而前端forloop.counter是从0开始,所以每页序号起始值要加上(n-1)*10 obj_id_start = ( self .page_id - 1 ) * self .page_max return obj_id_start @property def page_start( self ): # 每页的页码范围 page_start = ( self .page_id - 1 ) / / self .obj_max * self .page_max + 1 return page_start @property def page_end( self ): page_end = self .page_start + self .page_max # 如果结束页码大于总页码,就以总页码数为结尾 if page_end > self .page_total: page_end = self .page_total + 1 return page_end @property def page_previous( self ): # 向前翻页,如果页面起始值为1,则向前翻页链接禁用 if self .page_start = = 1 : page_previous = '<li class="disabled"><a aria-label="Previous"><span aria-hidden="true">«</span></a></li>' else : page_previous = '<li><a href="?page=%s" aria-label="Previous"><span aria-hidden="true">«</span></a></li>' \ % ( self .page_start - self .obj_max) return mark_safe(page_previous) @property def page_next( self ): # 向后翻页,最后一页向后翻页链接禁用 if self .page_end = = self .page_total + 1 : page_next = '<li class="disabled"><a aria-label="Next"><span aria-hidden="true">»</span></a></li>' else : page_next = '<li><a href="?page=%s" aria-label="Next"><span aria-hidden="true">»</span></a></li>' \ % ( self .page_start + self .obj_max) return mark_safe(page_next) |
views.py文件中只需要实例化分页类并传参即可
def user_list(request): user_all = models.User.objects. all () #获取当前页面Url中的?page=id值,默认为1 page_id = int (request.GET.get( 'page' , 1 )) #实例化分页类 paginate = Paginate(user_all, page_id) #返回参数,使用对象.参数的方法调用,由于类中使用了@property装饰器将方法封装成属性,所以不需要加括号即可执行 return render(request, 'user/user_list.html' , { 'user_all' : user_all[paginate.obj_start:paginate.obj_end], 'page_total' : range (paginate.page_start, paginate.page_end), # 显示页码范围 'obj_id_start' : paginate.obj_id_start, 'page_id' : paginate.page_id, 'page_previous' : paginate.page_previous, 'page_next' : paginate.page_next, }) |
user_list.html前端渲染
{ #分页#} <div> <nav aria - label = "Page navigation" > <ul class = "pagination" > { #向前翻页链接#} {{ page_previous }} { % for num in page_total % } { #num等于当前页码时,显示active#} <li { % if page_id = = num % } class = "active" { % endif % }><a href = "?page={{ num }}" >{{ num }}< / a>< / li> { % endfor % } { #向后翻页链接#} {{ page_next }} < / ul> < / nav> < / div> |
二、Django内置Paginator类
导包 from django.core.paginator import Paginator
1、通过Paginate构造一个分页器实例,两个参数分别是将要分类的对象列表和每页显示的数量
paginate = Paginate(obj_list, per_page_number) 例如 user_all = models.User.objects.all() paginate = Paginator(user_all, 10)
paginate 对象属性
- count,对象总数
- num_pages,页码总数
- page_range,可以获取所有页码范围,从1开始page_range = paginate.page_range
常见错误:
- InvalidPage,page()传递无效页码
- PageNotAnInteger,page()传递的不是整数
- Empty,page()传递的值有效,但是没有数据
2、分页对象(具体某一页)获取,通过Paginator的page()方法获取
例如想要获取第1页的数据
user_obj = paginate.page(1)
user_obj对象属性:
- objects_list 当前页面中所有的数据对象
- number 当前页的页码值
- paginate 当前page关联的Paginator对象,如果一个页面中有多个分页器时使用
user_obj对象方法:
- has_previous() 判断是否有上一页
- has_next() 判断是否有下一页
- has_other_pages()判断是否有上一页或下一页
- privious_page_number 返回上一页的页码
- next_page_number() 返回下一页的页码
- len() 返回当前页的数据个数
例子:
views.py
from django.views.generic import TemplateView from django.core.paginator import Paginator from user import models class UserList(TemplateView): template_name = 'user_list.html' def get_context_data(self, **kwargs): page = int(self.request.GET.get('page')) context = super().get_context_data(**kwargs) user_all = models.User.objects.all() # 定义分页器对象,筛选页面为page的对象 paginate = Paginator(user_all, 10) context['users_page'] = paginate.page(page) # 定义开始页码和结束页面,每次只显示10个 div, mod = divmod(page, 10) page_start = div * 10 + 1 if mod else div page_end = page_start + 10 if (page_start + 10) < paginate.num_pages else paginate.num_pages + 1 context['page_range'] = range(page_start, page_end) # 判断是否可以向前翻10页和向后翻10页 if page_start > 10: context['page_start_privious'] = page_start if page_start + 10 < paginate.num_pages: context['page_start_next'] = page_start return context
user_list.html
{% extends 'base.html' %} {% block body %} <table class="table"> <tr> <td>id</td> <td>username</td> </tr> {% for user in users_page.object_list %} <tr> <td>{{ user.id }}</td> <td>{{ user.username }}</td> </tr> {% endfor %} </table> <nav aria-label="Page navigation"> <ul class="pagination"> {#向前翻10页#} {% if page_start_privious %} <li> <a href="{% url 'user_list' %}?page={{ page_start_privious|add:-10 }}" aria-label="Previous"> <span aria-hidden="true">«</span></a> </li> {% endif %} {#向前翻页#} {% if users_page.has_previous %} <li> <a href="{% url 'user_list' %}?page={{ users_page.previous_page_number }}" aria-label="Previous"> <span aria-hidden="true">上一页</span></a> </li> {% else %} <li class="disabled"> <a href="#" aria-label="Previous"> <span aria-hidden="true">上一页</span> </a> </li> {% endif %} {# 页码 #} {% for page in page_range %} {#如果page与当前页码相等时,显示亮色#} <li {% ifequal page users_page.number %}class="active" {% endifequal %}><a href="{% url 'user_list' %}?page={{ page }}">{{ page }}</a></li> {% endfor %} {#向后翻页#} {% if users_page.has_next %} <li> <a href="{% url 'user_list' %}?page={{ users_page.next_page_number }}" aria-label="Next"> <span aria-hidden="true">下一页</span> </a> </li> {% else %} <li class="disabled"> <a href="#" aria-label="Next"> <span aria-hidden="true">下一页</span> </a> </li> {% endif %} {# 向后翻10页 #} {% if page_start_next %} <li> <a href="{% url 'user_list' %}?page={{ page_start_next|add:10 }}" aria-label="Next"> <span aria-hidden="true">»</span> </a> </li> {% endif %} </ul> </nav> {% endblock %}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用