Django-分页
分页
实质:分批获取数据
获取数据语句:models.UserInfo.objects.all()
分批获取直接在后面切片:models.UserInfo.objects.all()[0:10]
Django自带的分页
首先创建出300条UserInfo表的数据:
for i in range(300):
name = 'lxyker %d' % i
models.UserInfo.objects.create(name=name)
在index页面显示所有用户,views.py如下:
def index(request):
"""
分页
:param request:
:return:
"""
user_list = models.UserInfo.objects.all()
return render(request, 'index.html', {'user_list': user_list})
模板中使用for循环,将数据展示出来,index.html如下:
<h1>用户列表</h1>
<ul>
{% for user in user_list %}
<li>{{ user.name }}</li>
{% endfor %}
</ul>
如此,会在页面上直接显示出全部数据:
我们的目标是:每次显示10条数据,数据下方有按钮可以点击,每次点击直接跳转到对应页数。
首先导入django自带的分页工具包:from django.core.paginator import Paginator, page
创建一个paginator对象,在pycharm中按快捷键CTRL+P,查看需要填入的参数:
这个对象可以点出一些功能,具体查看注释部分,views.py如下:
def index(request):
"""
分页
:param request:
:return:
"""
user_list = models.UserInfo.objects.all()
paginator = Paginator()
# .per_page: 每页显示条目数量
# .count: 数据总个数
# .num_pages: 总页数
# .page_range: 总页数的索引范围,如: (1,10),(1,200)
# .page: page对象
return render(request, 'index.html', {'user_list': user_list})
其中.page可以用pg = paginator.page(1)
得到一个新的对象pg,表示展示第1页的数据。
这个pg对象有如下方法:
pg = paginator.page(1) # 表示当前显示第1页,这里的pg是另外一个对象,它的方法如下:
# .has_next 是否有下一页
# .next_page_number 下一页页码
# .has_previous 是否有上一页
# .previous_page_number 上一页页码
# .object_list 分页之后的数据列表
# .number 当前页
# .paginator paginator对象
假设我们从用户的GET请求中,获取到当前应该显示的页码数current_page:
current_page = request.GET.get('page')
将其作为参数放入pg对象中:pg = paginator.page(current_page)
pg.object_list
就是分页后的数据列表,那么我们可以将pg直接交给模板,然后用模板语法点出object_list。完整views.py如下:
from django.shortcuts import render
from django.core.paginator import Paginator, Page
from App import models
def index(request):
current_page = request.GET.get('page')
user_list = models.UserInfo.objects.all()
paginator = Paginator(user_list, 10)
pg = paginator.page(current_page)
return render(request, 'index.html', {'pg': pg})
index.html如下:
<h1>用户列表</h1>
<ul>
{% for user in pg.object_list %}
<li>{{ user.name }}</li>
{% endfor %}
</ul>
如此,访问http://localhost:8000/app/index?page=2就可以看到第2页的用户数据:
然后,在模板中添加“上一页”和“下一页”两个按钮,index.html部分代码如下:
<div>
{% if pg.has_previous %}
<a href="/app/index/?page={{ pg.previous_page_number }}">上一页</a>
{% endif %}
{% if pg.has_next %}
<a href="/app/index/?page={{ pg.next_page_number }}">下一页</a>
{% endif %}
</div>
再然后,利用for循环,将pg.paginator.page_range
将页码展示出来:
{% for page in pg.paginator.page_range %}
<a href="/app/index/?page={{ page }}">{{ page }}</a>
{% endfor %}
效果如图:
可惜django只能帮我们到这里了。
自定义分页组件
1、每次显示10条数据
核心代码:models.UserInfo.objects.all()[start:end]
GET方式传递当前页码数current_page,确定好每页显示多少条数据per_page。
找到规律后直接渲染到custom.html中。视图函数代码如下:
def custom(request):
# 表示用户当前想要访问的页面
current_page = int(request.GET.get('page'))
# 每页显示10条数据
per_page = 10
# 1: 0-10
# 2: 10-20
# 3: 20-30
# ……
start = (current_page - 1) * per_page
stop = current_page * 10
user_list = models.UserInfo.objects.all()[start:stop]
return render(request, 'custom.html', {'user_list': user_list})
2、封装简化
做一个类,将current_page、per_page作为属性,并且当用户输入的当前页不为整数、或者小于1时,都直接定位1。start、stop作为方法:
class PageInfo(object):
def __init__(self, current_page, per_page):
try:
self.current_page = int(current_page)
except Exception as e:
self.current_page = 1
if self.current_page < 1:
self.current_page = 1
self.per_page = per_page
def start(self):
return (self.current_page - 1) * self.per_page
def stop(self):
return self.current_page * self.per_page
于是简化custom函数:
def custom(request):
page_info = PageInfo(request.GET.get('page'), 10)
user_list = models.UserInfo.objects.all()[page_info.start():page_info.stop()]
return render(request, 'custom.html', {'user_list': user_list})
3、数据下方显示所有页码
首先,我们在PageInfo
类中再增加all_count
属性,表示数据总个数,用all_pager
表示一共有多少页;
新增pager
方法,让它直接返回字符串"<a href='/app/custom/?page=1'>1</a>"
;
在custom
中直接将对象page_info
传递到前端,然后在前端用{{ page_info.pager|safe }}
将pager
中的字符串渲染成a标签。注意,这里的page_info.pager
不需要加括号,模板中自动执行并将返回值拿出来。
views.py如下:
class PageInfo(object):
def __init__(self, current_page, all_count, per_page):
"""
:param current_page: 当前页码
:param all_count: 数据总个数
:param per_page: 每页显示的个数
"""
try:
self.current_page = int(current_page)
except Exception as e:
self.current_page = 1
if self.current_page < 1:
self.current_page = 1
self.per_page = per_page
self.all_count = all_count
self.all_pager = math.ceil(self.all_count / self.per_page)
def start(self):
return (self.current_page - 1) * self.per_page
def stop(self):
return self.current_page * self.per_page
def pager(self):
v = "<a href='/app/custom/?page=1'>1</a>"
return v
def custom(request):
all_count = models.UserInfo.objects.count()
page_info = PageInfo(request.GET.get('page'), all_count, 10)
user_list = models.UserInfo.objects.all()[page_info.start():page_info.stop()]
return render(request, 'custom.html', {'user_list': user_list, 'page_info': page_info})
给a标签加样式,改善pager()方法:
def pager(self):
s = str()
for i in range(1, self.all_pager+1):
if i == self.current_page:
temp = "<a style='display:inline-block;padding:5px;margin:5px;background-color:lightsalmon;' href='/app/custom/?page=%d'>%d</a>" % (i, i)
else:
temp = "<a style='display:inline-block;padding:5px;margin:5px;' href='/app/custom/?page=%d'>%d</a>" % (i, i)
s += temp
return s
此时效果如下:
4、完善与优化
让页面显示当前页的前后5条数据:
half = int(self.show_page/2)
begin = self.current_page - half
end = self.current_page + half + 1
for i in range(begin, end):
pass
其中,self.show_page
是我们在__init__
方法中新增的属性,表示我们期望展示11个页码。
此时,只需要修改for循环中的begin和end,就可以控制页面的起始页码和结尾页码。
我们根据current_page
的边界不一样,来判断应该展示的页码状态。具体判断方式如下代码所示:
half = int(self.show_page/2)
# 如果总数据比较少,导致:self.all_pager < self.show_page
if self.all_pager <= self.show_page:
begin = 1
end = self.all_pager + 1
else:
# 如果当前页比较靠前,比如self.current_page = 1之类
if self.current_page <= half:
begin = 1
end = self.show_page + 1
# 如果当前页比较靠后:
elif self.current_page > self.all_pager - half:
begin = self.all_pager - self.show_page + 1
end = self.all_pager + 1
# 中间情况:
else:
begin = self.current_page - half
end = self.current_page + half + 1
for i in range(begin, end):
pass
最后,在字符串s的前后,加上上一页和下一页的标签,注意要判断当前页是否有上一页或下一页:
prev_page = "<a style='display:inline-block;padding:5px;margin:5px;'href='/app/custom/?page=%d'>上一页</a>" % (self.current_page - 1 if self.current_page > 1 else 1)
s += prev_page
for i in range(begin, end):
pass
next_page = "<a style='display:inline-block;padding:5px;margin:5px;' href='/app/custom/?page=%d'>下一页</a>" % (self.current_page + 1 if self.current_page < self.all_pager else self.all_pager)
s += next_page
以上,还可以将url作为一个参数,以此生成a标签时,用%s
将url传递给a标签。
5、Bootstrap美化
bootstrap官网样例:
https://v3.bootcss.com/components/#pagination-default
我们只需要在a标签前增加li标签,再将官网样例的li标签删掉即可。
<nav aria-label="Page navigation">
<ul class="pagination">
{{ page_info.pager|safe }}
</ul>
</nav>
将PageInfo类放入工具箱,其他地方需要使用时,直接导入即可:
pager.py
代码如下:
import math
class PageInfo(object):
def __init__(self, current_page, all_count, per_page, show_page=5):
"""
:param current_page: 当前页码
:param all_count: 数据总个数
:param per_page: 每页显示的个数
:param show_page: 规定显示多少个页码
"""
try:
self.current_page = int(current_page)
except Exception as e:
self.current_page = 1
if self.current_page < 1:
self.current_page = 1
self.per_page = per_page
self.all_count = all_count
self.all_pager = math.ceil(self.all_count / self.per_page)
self.show_page = show_page
def start(self):
return (self.current_page - 1) * self.per_page
def stop(self):
return self.current_page * self.per_page
def pager(self):
s = str()
half = int(self.show_page/2)
# 如果总数据比较少,导致:self.all_pager < self.show_page
if self.all_pager <= self.show_page:
begin = 1
end = self.all_pager + 1
else:
# 如果当前页比较靠前,比如self.current_page = 1之类
if self.current_page <= half:
begin = 1
end = self.show_page + 1
# 如果当前页比较靠后:
elif self.current_page > self.all_pager - half:
begin = self.all_pager - self.show_page + 1
end = self.all_pager + 1
# 中间情况:
else:
begin = self.current_page - half
end = self.current_page + half + 1
prev_page = "<li><a href='/app/custom/?page=%d'>上一页</a></li>" % (self.current_page - 1 if self.current_page > 1 else 1)
s += prev_page
for i in range(begin, end):
if i == self.current_page:
temp = "<li class='active'><a href='/app/custom/?page=%d'>%d</a></li>" % (i, i)
else:
temp = "<li><a href='/app/custom/?page=%d'>%d</a></li>" % (i, i)
s += temp
next_page = "<li><a href='/app/custom/?page=%d'>下一页</a></li>" % (
self.current_page + 1 if self.current_page < self.all_pager else self.all_pager)
s += next_page
return s
最终效果如下: