自定义分页器
针对上一小节批量插入的数据,我们在前端展示的时候发现一个很严重的问题,一页展示了所有的数据,数据量太大,查看不方便
针对数据量大但又需要全部展示给用户观看的情况下,我们统一做法都是做分页处理
分页推导
首先我们需要明确的时候,get请求也是可以携带参数的,所以我们在朝后端发送查看数据的同时可以携带一个参数告诉后端我们想看第几页的数据
其次我们还需要知道一个点,queryset对象是支持索引取值和切片操作的,但是不支持负数索引情况
接下来我们就可以推导我们的自定义分页器步骤了
current_page = request.GET.get("page",1) # 获取用户想访问的页码 如果没有 默认展示第一页 | |
try: # 由于后端接受到的前端数据是字符串类型所以我们这里做类型转换处理加异常捕获 | |
current_page = int(current_page) | |
except Exception as e: | |
current_page = 1 | |
# 还需要定义页面到底展示几条数据 | |
per_page_num = 10 # 一页展示10条数据 | |
# 需要对总数据进行切片操作 需要确定切片起始位置和终止位置 | |
start_page = ? | |
end_page = ? | |
""" | |
下面需要研究current_page、per_page_num、start_page、end_page四个参数之间的数据关系 | |
per_page_num = 10 | |
current_page start_page end_page | |
1 0 10 | |
2 10 20 | |
3 20 30 | |
4 30 40 | |
per_page_num = 5 | |
current_page start_page end_page | |
1 0 5 | |
2 5 10 | |
3 10 15 | |
4 15 20 | |
可以很明显的看出规律 | |
start_page = (current_page - 1) * per_page_num | |
end_page = current_page* per_page_num | |
""" |
数据总页面获取
当我问你下面几个问题的时候,你的内心肯定是鄙视的,不信的话那就请听题
问题1:总数据有100条,每页展示10条,总共需要几页?
答案:10条
问题2:总数据有101条,每页展示10条,总共需要几页?
答案:11条
问题3:如何通过代码算出到底需要多少条?
答案:去你妹的,不会!!!
内置方法之divmod
>>> divmod(100,10) | |
(10, 0) # 10页 | |
>>> divmod(101,10) | |
(10, 1) # 11页 | |
>>> divmod(99,10) | |
(9, 9) # 10页 | |
# 余数只要不是0就需要在第一个数字上加一 |
我们可以判断元祖的第二个数字是否为0从而确定到底需要多少页来展示数据
book_queryset = models.Book.objects.all() | |
all_count = book_queryset.count() # 数据总条数 | |
all_pager, more = divmod(all_count, per_page_num) | |
if more: # 有余数则总页数加一 | |
all_pager += 1 |
至此分页器大致的功能及思路我们就已经大致清楚了
最后我们只需要利用start_page和end_page对总数据进行切片取值再传入前端页面就能够实现分页展示
book_list = models.Book.objects.all()[start_page:end_page] | |
return render(request,'booklist.html',locals()) |
接下来就是前端页面的代码编写了
{% for book in book_list %} | |
<p>{{ book.title }}</p> | |
{% endfor %} |
现在我们实现了最简单的分页,但是前端没有按钮去让用户点击需要看第几页,所以我们需要渲染分页器相关代码,这里我们不做要求直接去bootstrap框架拷贝代码即可
终极大法
上面是自定义分页器开发流程的基本思路,我们不需要掌握代码的编写,只需要掌握基本用法即可
自定义分页器封装代码
class Pagination(object): | |
def __init__(self, current_page, all_count, per_page_num=2, pager_count=11): | |
""" | |
封装分页相关数据 | |
:param current_page: 当前页 | |
:param all_count: 数据库中的数据总条数 | |
:param per_page_num: 每页显示的数据条数 | |
:param pager_count: 最多显示的页码个数 | |
""" | |
try: | |
current_page = int(current_page) | |
except Exception as e: | |
current_page = 1 | |
if current_page < 1: | |
current_page = 1 | |
self.current_page = current_page | |
self.all_count = all_count | |
self.per_page_num = per_page_num | |
# 总页码 | |
all_pager, tmp = divmod(all_count, per_page_num) | |
if tmp: | |
all_pager += 1 | |
self.all_pager = all_pager | |
self.pager_count = pager_count | |
self.pager_count_half = int((pager_count - 1) / 2) | |
@property | |
def start(self): | |
return (self.current_page - 1) * self.per_page_num | |
@property | |
def end(self): | |
return self.current_page * self.per_page_num | |
def page_html(self): | |
# 如果总页码 < 11个: | |
if self.all_pager <= self.pager_count: | |
pager_start = 1 | |
pager_end = self.all_pager + 1 | |
# 总页码 > 11 | |
else: | |
# 当前页如果<=页面上最多显示11/2个页码 | |
if self.current_page <= self.pager_count_half: | |
pager_start = 1 | |
pager_end = self.pager_count + 1 | |
# 当前页大于5 | |
else: | |
# 页码翻到最后 | |
if (self.current_page + self.pager_count_half) > self.all_pager: | |
pager_end = self.all_pager + 1 | |
pager_start = self.all_pager - self.pager_count + 1 | |
else: | |
pager_start = self.current_page - self.pager_count_half | |
pager_end = self.current_page + self.pager_count_half + 1 | |
page_html_list = [] | |
# 添加前面的nav和ul标签 | |
page_html_list.append(''' | |
<nav aria-label='Page navigation>' | |
<ul class='pagination'> | |
''') | |
first_page = '<li><a href="?page=%s">首页</a></li>' % (1) | |
page_html_list.append(first_page) | |
if self.current_page <= 1: | |
prev_page = '<li class="disabled"><a href="#">上一页</a></li>' | |
else: | |
prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,) | |
page_html_list.append(prev_page) | |
for i in range(pager_start, pager_end): | |
if i == self.current_page: | |
temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,) | |
else: | |
temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,) | |
page_html_list.append(temp) | |
if self.current_page >= self.all_pager: | |
next_page = '<li class="disabled"><a href="#">下一页</a></li>' | |
else: | |
next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,) | |
page_html_list.append(next_page) | |
last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,) | |
page_html_list.append(last_page) | |
# 尾部添加标签 | |
page_html_list.append(''' | |
</nav> | |
</ul> | |
''') | |
return ''.join(page_html_list) |
自定义分页器使用
后端
def get_book(request): | |
book_list = models.Book.objects.all() | |
current_page = request.GET.get("page",1) | |
all_count = book_list.count() | |
page_obj = Pagination(current_page=current_page,all_count=all_count,per_page_num=10) | |
page_queryset = book_list[page_obj.start:page_obj.end] | |
return render(request,'booklist.html',locals()) |
前端
<div class="container"> | |
<div class="row"> | |
<div class="col-md-8 col-md-offset-2"> | |
{% for book in page_queryset %} | |
<p>{{ book.title }}</p> | |
{% endfor %} | |
{{ page_obj.page_html|safe }} | |
</div> | |
</div> | |
</div> |