分页:Paginator
提要:列表分页主要是明白:列表当前需要显示内容索引 =(列表当前页数-1)*每页显示条数 至 列表当前页数*每页显示条数
一、Django的内置分页
Django内置的分页需要使用到3个类:Paginator、EmptyPage、PageNotAnInteger
1.1 Paginator介绍
paginator = Paginator(全部数据, 每页显示条数)
paginator对象下面包括属性per_page、count、num_pages、page对象;
page对象下面包括属性:
has_next、next_page_number、has_previous、previous_page_number、number、object_list、paginator对象
下图为各个对象属性的关系及注释:
1.2 EmptyPage、PageNotAnInteger介绍
EmptyPage、PageNotAnInteger这两个类是为了防止p值(当前页码)不是int类型、或者大于总页码的情况;
1.3 简单截取实例
下面简单截取部分代码诠释:
……
from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger
def……下面为函数的函数体:
#数据库数据
stu_list=models.Student.objects.all()
cls_list=models.Classes.objects.all()
#分页
current_page = request.GET.get('p')
#从请求连接中get参数中获取p值,即:当前页码;
所以分页的请求需包含p参数,如:href="/students?p={{ posts.previous_page_number }}"
current_page = int(current_page)
#
paginator = Paginator(stu_list, 10)
# 全部数据
# per_page:每页显示条目数量
# count:数据总个数
# num_pages:总页数
# page_range:总页数的索引范围
# page:page对象(封装了几个属性:是否具有下一页;是否具有上一页;以及下一页/下一页的页码)
try:
#page对象
posts = paginator.page(current_page)
#has_next 是否有下一页
#next_page_number 下一页页码
#has_previous 是否有上一页
#previous_page_number 上一页页码
#object_list 分页之后的数据列表,已经切片好的数据
#number 当前页
#paginator paginator对象
except PageNotAnInteger:
posts = paginator.page(1)
except EmptyPage:
posts = paginator.page(paginator.num_pages)
#pages对象包含了全部的分页需要使用到的属性,包括paginator对象,所以只需将其传入html文件即可;
return render(request,"students.html",{"cls_list":cls_list,"posts":posts})
html文件:
在对应html下include导入pager.html,页面显示:
二、扩展Django内置分页
2.1 继承Paginator类
扩展Django内置分页,主要继承Paginator类,传入参数:current_page(当前页码)、per_pager_num(自定制每页显示页码个数);
之后在对应函数中实例化就只需调用子类CustomerPaginator,传入4个参数:current_page、per_pager_num、全部数据、每页显示条数;
from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger
class CustomerPaginator(Paginator):
'''
继承Paginator类,用于保留原有功能、并扩展新的功能
'''
def __init__(self,current_page,per_pager_num,*args,**kwargs):
super(CustomerPaginator,self).__init__(*args,**kwargs)
self.current_page = int(current_page) #当前页码
self.per_page_num = int(per_pager_num) #分页自定制显示最多页码个数
def pager_num_range(self):
'''
CustomerPaginator自定制新功能:显示分页页码
:return:
'''
if self.num_pages < self.per_page_num:
#当数据的总页码数<自定制的每页显示分页页码数时,显示:1到总页码数
return range(1,self.num_pages+1)
#自定制每页显示页码数/2,取整,主要是想当前页码显示在页码中央:
part = int(self.per_page_num/2)
if self.current_page <= part:
#如果当前页码小于自定制每页显示页码数的一半
return range(1, self.per_page_num + 1)
if self.current_page + part > self.num_pages:
# 如果当前页码加上自定制每页显示页码数的一半,大于总页码数:
return range(self.num_pages - self.per_page_num + 1,self.num_pages + 1)
#在上面的极端值情况后,常规情况是取当前页前后-+part范围
return range(self.current_page - part, self.current_page + part + 1)
def students(request):
'''
学生页面
:param request:
:return:
'''
stu_list=models.Student.objects.all()
cls_list=models.Classes.objects.all()
#分页
current_page = request.GET.get('p')
current_page = int(current_page)
#
#由于上面使用CustomerPaginator继承了Paginator,所以这里使用CustomerPaginator实例化,传入4个参数:当前页码、每页显示页码个数、全部数据、每页显示数据条数
paginator = CustomerPaginator(current_page,11,stu_list, 10)
try:
#page对象
posts = paginator.page(current_page)
except PageNotAnInteger:
posts = paginator.page(1)
except EmptyPage:
posts = paginator.page(paginator.num_pages)
return render(request,"students.html",{"cls_list":cls_list,"posts":posts})
2.2 前端HTML调用
<ul class="pagination" style="margin-top: 0px">
<li> <a href="/students.html?p=1" aria-label="F">
<span aria-hidden="true">首页</span>
</a></li>
{% if posts.has_previous %}
<li> <a href="/students.html?p={{ posts.previous_page_number }}" aria-label="Previous">
<span aria-hidden="true">上一页</span>
</a></li>
{% else %}
<li> <a href="#" aria-label="Previous">
<span aria-hidden="true">上一页</span></a>
</li>
{% endif %}
{% for i in posts.paginator.pager_num_range %}
{% if i == posts.number %}
<li> <a style="background-color: #9acfea;" href="/students.html?p={{ i }}">{{ i }}</a></li>
{% else %}
<li><a href="/students.html?p={{ i }}">{{ i }}</a></li>
{% endif %}
{% endfor %}
{% if posts.has_next %}
<li><a href="/students.html?p={{ posts.next_page_number }}" aria-label="Next">
<span aria-hidden="true">下一页</span>
</a></li>
{% else %}
<li><a href="#" aria-label="Next">
<span aria-hidden="true">下一页</span>
</a></li>
{% endif %}
<li> <a href="/students.html?p={{ posts.paginator.num_pages }}" aria-label="Last">
<span aria-hidden="true">尾页</span>
</a></li>
</ul>
三、自定义分页组件
从上面的代码可以看出有两个未解的问题:
1.前端分页的html中的a标签的href链接没有参数化、动态化;还不能百分之百实现别人拿来少部分修改就可以使用;所以,下面自己封装一个类(分页组件);
2.分页类中对象包含了查询的所有数据,万一数据有很多,这样会很浪费性能(针对别的框架哈,django框架不会,因为django是你什么时候需要数据,它才执行)但是还是需要解决;
分页组件代码及部分注释:
class Pagination(object):
'''
自己封装分页组件,别人拿来就可以使用的,需要条件bootstrap、js、若非bootstrap(如layui)请自行修改标签的class属性
'''
def __init__(self,totalCount,currentPage,modelsName,perPageItrmNum= 10,maxPageNum= 7):
'''
初始化,传入5个参数
:param totalCount: 数据总条数
:param currentPage: 当前页
:param modelsName: 页码所属模块名,关联url,例举:href='/%s.html?p=%s'%(self.models_name,1)>
:param perPageItrmNum: 每页显示条数,默认值10
:param maxPageNum: 最多显示页码个数,默认值7
'''
self.total_count=totalCount
#下面这个对current_page当前页码的初始化使用try,是为了防止current_page不是int类型值或小于1,否则赋值=1
try:
pagenum=int(currentPage)
if pagenum<=0:
pagenum=1
self.current_page = pagenum
except EmptyPage as e:
self.current_page = 1
self.models_name=modelsName
self.per_page_item_num = perPageItrmNum
self.max_page_num = maxPageNum
@property
def start(self):
'''
需要显示的数据的起始索引
:return: (当前页-1)* 每页显示条数
'''
return (self.current_page-1)*self.per_page_item_num
@property
def end(self):
'''
需要显示的数据的最后索引
:return:当前页 * 每页显示条数
'''
return self.current_page * self.per_page_item_num
@property
def num_pages(self):
'''
总页数
:return:
'''
#divmod(x,y),返回x/y的商a,余数b
a,b=divmod(self.total_count,self.per_page_item_num)
if b==0:
return a
return a+1
@property
def pager_num_range(self):
'''
Pagination自定制新功能:显示分页页码
:return:
'''
if self.num_pages < self.max_page_num:
#当数据的总页码数<自定制的每页显示分页页码数时,显示:1到总页码数
return range(1,self.num_pages+1)
#自定制每页显示页码数/2,取整,主要是想当前页码显示在页码中央:
part = int(self.max_page_num/2)
if self.current_page <= part:
#如果当前页码小于自定制每页显示页码数的一半
return range(1, self.max_page_num + 1)
if self.current_page + part > self.num_pages:
# 如果当前页码加上自定制每页显示页码数的一半,大于总页码数:
return range(self.num_pages - self.max_page_num + 1,self.num_pages + 1)
#在上面的极端值情况后,常规情况是取当前页前后-+part范围
return range(self.current_page - part, self.current_page + part + 1)
@property
def page_str_ui(self):
'''
将分页html拼接string,前端html直接调用,渲染
:return:
'''
page_list = []
#ul标签开头
page_list.append("<ul class='pagination pagination-sm' style='margin-top: 0px'>")
#首页
temp_first = "<li> <a href='/%s.html?p=1' aria-label='F'>" \
"<span aria-hidden='true'>首页</span></a></li>"%(self.models_name)
page_list.append(temp_first)
#上一页
if self.current_page == 1:
prev="<li> <a href='#'>" \
"<span aria-hidden='true'>上一页</span></a></li>"
else:
prev="<li> <a href='/%s.html?p=%s'>" \
"<span aria-hidden='true'>上一页</span></a></li>"%(self.models_name,self.current_page-1,)
page_list.append(prev)
#中间页码
for i in self.pager_num_range:
if i == self.current_page:
temp="<li> <a style='background-color: #9acfea;' href='/%s.html?p=%s'>" \
"<span aria-hidden='true'>%s</span></a></li>"%(self.models_name,i,i)
else:
temp = "<li> <a href='/%s.html?p=%s'>" \
"<span aria-hidden='true'>%s</span></a></li>" % (self.models_name, i, i)
page_list.append(temp)
#下一页
if self.current_page == self.num_pages:
nex="<li> <a href='#'>" \
"<span aria-hidden='true'>下一页</span></a></li>"
else:
nex="<li> <a href='/%s.html?p=%s'>" \
"<span aria-hidden='true'>下一页</span></a></li>"%(self.models_name,self.current_page+1,)
page_list.append(nex)
#尾页
temp_last = "<li> <a href='/%s.html?p=%s' aria-label='L'>" \
"<span aria-hidden='true'>尾页</span></a></li>"%(self.models_name,self.num_pages)
page_list.append(temp_last)
#ul结尾
page_list.append("</ul>")
return "".join(page_list)
使用该分页组件实例截图:
显示结果截图: