Django分页的实现

Django ORM 

分页介绍

分页是网页浏览中常见到的一种形式,在数据量较大时,一个页面显示不全,采取分割数据由用户选择进行显示的方式。

基本实现

技术点

  1. 通过切片得到数据库中数据的一部分来显示。即为分页当前页面显示的数据。
  2. 通过 GET 方法从 URL 当中获取当前页的页码,由页码进行切片分割。
  3. 页面中的页码由服务端直接返回字符串来显示。

前端代码

注: 其中的样式类使用的 bootstrap 的。

<div class="container">
<table class="table table-bordered">
<thead>
<tr>
<th>序号</th>
<th>id</th>
<th>书名</th>
</tr>
</thead>
<tbody>
{% for book in books %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ book.id }}</td>
<td>{{ book.title }}</td>
</tr>
{% endfor %} </tbody>
</table> <nav aria-label="Page navigation">
<ul class="pagination"> {{ page_html|safe }} </ul>
</nav>
</div>

Django views 的代码

def books(request):
all_books = models.Book.objects.all()
page_num = request.GET.get("pages")
page_num = int(page_num)
data_start = (page_num - 1) * 10
data_end = page_num * 10 total_count = models.Book.objects.all().count()
per_page = 10
total_page, m = divmod(total_count, per_page) if m > 0:
total_page += 1 print(total_count) page_books = all_books[data_start: data_end] page_html_list = [] for i in range(1, total_page+1):
temp = '<li><a href="/books/?pages={0}">{0}</a></li>'.format(i)
page_html_list.append(temp) # 转成字符串
page_html = "".join(page_html_list)
print(page_html) return render(request, "books.html", {"books": page_books, "page_html": page_html})

结果与分析

基本结果

可以看出,页面将全部的页码全都显示出来,不美观也没必要。

进一步改善需求:

  1. 当前页面仅显示一定数量的页码,页码的数量是固定的。
  2. 在页码数量固定的前提下,当前页码处于中间,前后有相同的页码数。且当前页码要为 active 状态。
  3. 增加首页、尾页页码,点击可跳转到首页及尾页。
  4. 增加前一页、后一页功能。点击可跳到前一页或者后一页。且当前页处于首页时前一页不可点击,同样,当前页处于尾页时后一页不可点击。

我的改善步骤

1. 先实现一半一半

# 设置最大页码数
max_page = 11
half_max_page = max_page // 2 # 开始页和尾页自然为当前页加上最大页码数的一半
page_start = page_num - half_max_page
page_end = page_num + half_max_page # 同时,生成 li 标签的循环也要进行更改
for i in range(page_start, page_end + 1):
temp = '<li><a href="/books/?pages={0}">{0}</a></li>'.format(i)
page_html_list.append(temp)

2. 特殊情况一:页码前面出现负值

因为是由当前值减去半大值得到的首页页码数,所以当当前页码小于一定值时会出现负值,此时将首页页码设置为 1 ,尾页页码设置为最大页码数。

if page_start < 1:
page_start = 1
page_end = max_page

3. 特殊情况二:页码后面出现空白页

同样,尾页页码大于一定值时,会显示出更多的页码但那些页码是超过数据范围的,当前页会显示空白页。

解决办法:首页页码设置为 总页数 - 最大页码数 + 1 ,尾页页码设置为总页数。

if page_end >= total_page:
page_start = total_page - max_page + 1
page_end = total_page

4. 加上首页

首页就是将 li 标签中的链接里的 href 固定为第一页的链接形式。

page_html_list.append('<li><a href="/books/?pages=1">首页</a></li>')

5. 加上尾页

尾页就是将 li 标签中的链接里的 href 固定为第最后一页的链接形式。

page_html_list.append('<li><a href="/books/?pages={0}">尾页</a></li>'.format(total_page))

6. 前一页

依旧是改链接,将链接中的 pages 值减一。

page_html_list.append('<li><a href="/books/?pages={}"><span aria-hidden="true">&laquo;</span></a></li>'.format(page_num - 1))

7. 加上后一页

依旧是改链接,将链接中的 pages 值加一。

page_html_list.append('<li><a href="/books/?pages={}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>'.format(page_num + 1))

8. 解决最后一页时点后一页

常理来说当前页处于最后一页时,后一页应该不允许点击。否则会出现页码不断增大,而页面无数据的情况。

if page_num == total_page:
page_html_list.append(
'<li class="disabled"><a href="#" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>')
else:
page_html_list.append(
'<li><a href="/books/?pages={}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>'.format(
page_num + 1))

9. 解决在首页处点前一页

常理来说当前页处于首页时,前一页应该不允许点击。否则会出现页码不断减小,出现负数,而页面无数据的情况。

if page_num == 1:
page_html_list.append(
'<li class="disabled"><a href="#"><span aria-hidden="true">&laquo;</span></a></li>')
else:
page_html_list.append(
'<li><a href="/books/?pages={}"><span aria-hidden="true">&laquo;</span></a></li>'.format(page_num - 1))

10. 如果数据量少,页数也少

当数据量较少,甚至不够最大页码数的时候,根据情况将最大页码数设置为总页数。

max_page = 11

if total_page < max_page:
max_page = total_page half_max_page = max_page // 2
page_start = page_num - half_max_page
page_end = page_num + half_max_page

11. 对当前页加上活动 active 样式类

这个很简单,如果当前页面的 pages 值等于页码数时,直接在 li 标签中加入 active 样式类即可,我这里使用的是 bootstrap 。

for i in range(page_start, page_end + 1):
if i == page_num:
temp = '<li class="active"><a href="/books/?pages={0}">{0}</a></li>'.format(i)
else:
temp = '<li><a href="/books/?pages={0}">{0}</a></li>'.format(i)
page_html_list.append(temp)

12. 实现对输入页码数非数字及超过总页码数的判断

处理下 URL 中 pages 值为非数字和大数字的问题。

  • 非数字:默认跳转到首页
  • 大数字(超过总页码数):默认跳到最后一页
try:
page_num = int(page_num)
# 如果输入页码数过大,默认跳到最后一页
if page_num > total_page:
page_num = total_page
except Exception as e:
page_num = 1

结果展示

改善后的分页

完整代码

def books(request):
all_books = models.Book.objects.all() page_num = request.GET.get("pages") total_count = models.Book.objects.all().count()
per_page = 10
total_page, m = divmod(total_count, per_page) if m > 0:
total_page += 1 # 12. 实现对输入页码数非数字及超过总页码数的判断
try:
page_num = int(page_num)
# 如果输入页码数过大,默认跳到最后一页
if page_num > total_page:
page_num = total_page
except Exception as e:
page_num = 1 data_start = (page_num - 1) * 10
data_end = page_num * 10 page_books = all_books[data_start: data_end] # 1. 先实现一半一半
max_page = 11
# 10. 如果数据量少,页数也少
if total_page < max_page:
max_page = total_page
half_max_page = max_page // 2
page_start = page_num - half_max_page
page_end = page_num + half_max_page # 2. 特殊情况一:页码前面出现负值
if page_start < 1:
page_start = 1
page_end = max_page # 3. 特殊情况二:页码后面出现空白页
if page_end >= total_page:
page_start = total_page - max_page + 1
page_end = total_page page_html_list = [] # 6. 前一页
# page_html_list.append('<li><a href="/books/?pages={}"><span aria-hidden="true">&laquo;</span></a></li>'.format(page_num - 1)) # 9. 解决在首页处点前一页
if page_num == 1:
page_html_list.append(
'<li class="disabled"><a href="#"><span aria-hidden="true">&laquo;</span></a></li>')
else:
page_html_list.append(
'<li><a href="/books/?pages={}"><span aria-hidden="true">&laquo;</span></a></li>'.format(page_num - 1)) # 4. 加上首页
page_html_list.append('<li><a href="/books/?pages=1">首页</a></li>') for i in range(page_start, page_end + 1):
# 11. 对当前页加上活动active样式类
if i == page_num:
temp = '<li class="active"><a href="/books/?pages={0}">{0}</a></li>'.format(i)
else:
temp = '<li><a href="/books/?pages={0}">{0}</a></li>'.format(i)
page_html_list.append(temp) # 5. 加上尾页
page_html_list.append('<li><a href="/books/?pages={0}">尾页</a></li>'.format(total_page)) # 7. 加上后一页
# page_html_list.append('<li><a href="/books/?pages={}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>'.format(page_num + 1)) # 8. 解决最后一页时点后一页
if page_num == total_page:
page_html_list.append(
'<li class="disabled"><a href="#" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>')
else:
page_html_list.append(
'<li><a href="/books/?pages={}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>'.format(
page_num + 1)) # 转成字符串
page_html = "".join(page_html_list) return render(request, "books.html", {"books": page_books, "page_html": page_html})

总结

能够成功实现分页,但,这在 views 当中的一个函数中实现的,可以看出代码量很大,现实中不可能只有一个数据表需要分页,每次都写这么多代码显然利用率很低。

下一步将分页封装成一个类,只需要根据需要实例化该类就能实现分类,利用率极大提升。

GitHub地址:https://github.com/protea-ban/oldboy/tree/master/s9day71/ormday71

Django分页的实现的更多相关文章

  1. django 分页(2) 使用类 页码显示

    django 分页显示页码 views.py 显示11页码 ) < 起始位置 - 10总页数 else 总页数 > IF 当前页 小于 起始位置 结束页 IF 当前页 大于 IF 如果结束 ...

  2. Django 分页功能

    Django 分页功能比较强大,这边是结合官网的内容写的可以参考 https://docs.djangoproject.com/en/1.9/topics/pagination/ 分页命令行练习案列 ...

  3. 原生的 django 分页

    原始的 django 分页 # 基本 写法 class Paginator(object): def __init__(self, object_list, per_page, orphans=0, ...

  4. django 分页组件

      一.仿django分页功能自己实现 urls.py 1 2 3 4 5 6 7 8 9 from django.conf.urls import url from django.contrib i ...

  5. Django分页(二)

    Django分页(二) 要求 .设定每页显示数据条数 # # .用户输入页码(第一页.第二页...) # # .设定显示多少页号 # # .获取当前数据总条数 # # .根据设定显示多少页号和数据总条 ...

  6. Django分页(一)

    Django分页(一) 手动实现简单分页 HTML <!DOCTYPE html> <html lang="en"> <head> <me ...

  7. django 分页出现 UnorderedObjectListWarning 错误

    django 分页出现此错误: UnorderedObjectListWarning: Pagination may yield inconsistent results with an unorde ...

  8. 2019.03.20 mvt,Django分页

    MVT模式   MVT各部分的功能:   M全拼为Model,与MVC中的M功能相同,负责和数据库交互,进行数据处理.       V全拼为View,与MVC中的C功能相同,接收请求,进行业务处理,返 ...

  9. Django分页类的封装

    Django分页类的封装 Django ORM  封装 之前有提到(Django分页的实现)会多次用到分页,将分页功能封装起来能极大提高效率. 其实不是很难,就是将之前实现的代码全都放到类中,将需要用 ...

随机推荐

  1. yum 系列(二) 离线部署

    yum 系列(二) 离线部署 一.下载 rpm 依赖包 (1) yum 下载 rpm 包 准备一台 全新的 CentOS7 mini 虚拟机 ,联网下载所有所需的 rpm 包和其依赖, yum ins ...

  2. Mockplus组件样式库一键解决风格复用

    在Mockplus3.3版本中,新增了组件样式库,可以快速复用组件风格,同时可以将组件风格保存到库中. 官网地址:https://www.mockplus.cn 1. 保存样式 选中组件,设置好该组件 ...

  3. linux下的shell运算(加、减、乘、除

    linux下的shell运算(加.减.乘.除 摘自:https://blog.csdn.net/hxpjava1/article/details/80719112 2018年06月17日 16:03: ...

  4. background image

    http://www.ajaxblender.com/bgstretcher-2-jquery-stretch-background-plugin-updated.html http://blog.d ...

  5. Ural 1519 Formula 1 (DP)

    题意:给定一个 n * m 的矩阵,问你能花出多少条回路. #pragma comment(linker, "/STACK:1024000000,1024000000") #inc ...

  6. UVA 11865 Stream My Contest (二分+最小树形图)

    题意:给定一个网络,一个服务器,其他的是客户机,有 m 条连线,每条有一个带宽和花费(单向边),让你用不超过 c 的花费,使得 0 到 所有的机器都能到达,并且使得最小带宽最大. 析:很明显是二分题, ...

  7. Mysql命令行查看数据库大小(数据库版本为5.7以上)

    数据库版本为5.7以上1.选择数据库use mydb1; 2.查看指定数据库表结构select * from information_schema.TABLES where information_s ...

  8. (转)SQL Server内存遭遇操作系统进程压榨案例

    原文地址:http://www.cnblogs.com/zc_0101/p/3592259.html 场景: 最近一台DB服务器偶尔出现CPU报警,我的邮件报警阈(请读yù)值设置的是15%,开始时没 ...

  9. 第二届CCCC赛后感想 2017-04-15 23:56 88人阅读 评论(0) 收藏

    第一次写赛后感想,也不算什么很正规的比赛,不过这次比赛的时间恰好处于思想变化的阶段,留贴纪念. 先谈谈这次比赛,弱校萌新,依靠申请进了总决赛,发现和第一届不一样,缺少了团队奖心中有点缺乏动力,比赛2个 ...

  10. 网络排错与网络命令的理解ping-traceroute-host(nslookup)-tcpdump获取对方的mac

    1.  虚拟机中NAT架构的网络结构中, 虚拟网卡VMnet8(192.168.134.1)是连接宿主主机. 用虚拟网段中主机(192.168.134.133),ping  VMnet8 为什么没有响 ...