Django 自定义分页器实现原理与代码详解
本文深入解析 Django 中自定义分页器的实现机制。从前端 GET 请求获取页码开始,详细推导当前页、每页条数、起止索引及总页数(利用 divmod)之间的数学关系。通过封装 Python 类,结合后端数据切片与前端 HTML 动态生成页码导航,提供了一套完整且可复用的分页解决方案,适用于数据量较大的列表展示场景。
1.什么是自定义分页器
当页面需要展示大量数据时,将所有内容堆砌在单页上既不美观也不利于用户浏览。此时,引入自定义分页器成为必要手段。其核心作用是将庞大的数据集切割为多个页面,每个页面固定展示指定数量的数据条目,从而提升加载速度与交互体验。
2.为什么要用自定义分页器
正如前文所述,使用分页器的主要目的是实现数据的分页展示。这不仅便于用户逐页阅读,还能显著降低单次请求的数据传输量,提高查询效率,是处理大数据量列表的标准做法。
3.如何使用自定义分页器
3.1 自定义分页器推导过程
尽管市面上已有封装完善的分页插件,直接复制粘贴即可使用,但作为开发者,理解其底层运行逻辑至关重要。只有掌握核心原理,才能根据具体业务需求进行灵活调整与优化。
首先需明确,前端向后端发起请求时,GET 和 POST 是两种常见方式。在分页场景中,必须采用 GET 请求将用户希望跳转的目标页码传递给后端接口,以便服务端据此计算并返回对应的数据片段。
其次,Django 中的 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
"""
在明确了当前页码(current_page)、每页展示数据条数(per_page_num)、以及每页数据的起始位置(start_page)和结束位置(end_page)这四个关键变量后,还有一个至关重要的参数需要确定:
数据库中被查询的总数据量究竟是多少条?这是计算总页数的基础。
为此,我们需要利用 Python 内置函数 divmod。该函数用于计算两个数相除的结果,同时返回商和余数。例如:
>>> divmod(100,10)
(10, 0) # 10页
>>> divmod(101,10)
(10, 1) # 11页
>>> divmod(99,10)
(9, 9) # 10页
# 余数只要不是0就需要在第一个数字上加一
利用 divmod 的结果,我们可以准确判断总共需要生成多少页。若余数不为零,则总页数需在商的基础上加 1。
后端自定义分页器逻辑详解:
def book(request):
if request.method == 'GET':
current_page = request.GET.get('page',1) # 获取用户需要访问的页面,如果没有默认返回1
try: #异常捕获,因为前端返回的都是字符串,需要把他们都转成数字类型,方便下面做运算操作
current_page = int(current_page)
except Exception as e:
current_page = 1 # 用户输入啥字母等也默认为1
per_page_num = 10 #每页展示多少条数据
start_page = (current_page - 1) * per_page_num #当前页数起始数据
end_page = current_page * per_page_num #当前页结束数据
book_num = models.Book.objects.all() #将所有数据查询出来
all_count = book_num.count() # 统计一共有多少数据
num,more = divmod(all_count,per_page_num) #divmod方法计算需要的总页数
if more:
all_page = num + 1 #more为余数,余为0,则刚刚好是num页数,不为0,则页数加1
# 然后我们需要在html页面的分页器标签部分,for循环一下总共需要的num页数,但是有一个问题是,前端无法使用range()
# 这就需要我们在后端进行循环,再传到前端
html = ''
a = current_page # 为了下面页码高亮调整
if current_page <6: #当页面小于6时,固定在6上面,为下面的for处理不出现页码负数
current_page = 6
# 访问第6页时,只会显示当前页-5和+6的底部页码数,但是当页面小于6时,页码会出现0甚至负数,所有我们需要对页面进行一个设置,就是上述的a
for i in range(current_page-5,current_page+6):
if a == i:
# 当前页的页面高亮显示
html += '<li class="active"><a href="?page=%s" rel="external nofollow" rel="external nofollow" >%s</a></li>' % (i, i)
else:
html += '<li><a href="?page=%s" rel="external nofollow" rel="external nofollow" >%s</a></li>' % (i, i)
book_queryset = book_num[start_page:end_page]
return render(request,'book.html',locals())
前端页面部分:
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
<a href="#" rel="external nofollow" rel="external nofollow" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{{ html|safe }}
<li>
<a href="#" rel="external nofollow" rel="external nofollow" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
3.2 自定义分页器封装代码
以下是封装好的分页器核心代码实现。
3.3 封装好分页器的使用
后端处理逻辑如下:
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>至此,关于 Django 自定义分页器的实现示例介绍完毕。如需了解更多 Django 相关技巧,建议查阅本站过往文章或继续浏览后续内容。感谢大家的支持与关注!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









