前言
讲解python的分页器之前,科普一下java的分页器
每门编程语言都有一个分页的工具,类似java有PageHelper分页插件
Mybatis章节中有具体阐述:Mybatis从入门到精通(全)
对于实战中的引入,也可看我这篇文章:米米商城项目实战(含项目源码)
而Paginator是Django的一个分页工具,通过分页工具进行分页,将其数据源获取之后,定义每页显示的个数拆分(其他编程语言的分页工具大同小异),了解其逻辑原理以及函数即可
这些函数主要位于django/core/paginator.py
官网说明文档:Paginator的说明文档
主要的函数源码:Source code for django.core.paginator
1. 函数讲解
熟知一个功能使用,了解其函数源码必不可少
1.1 Paginator类
Paginator的函数讲解:
class Paginator(object_list, per_page, orphans=0, allow_empty_first_page=True)
其参数说明:
- object_list(必选项):一个列表、元组、QuerySet或其他具有count()或__len__()方法的可切片对象
- per_page(必选项):页面上要包含的最大条目数
- orphans(可选):默认为0,表示最后一页不会合并,可能只有一条数据。如果最后一页小于或者等于orphans,前一页的数据添加在最后一页成为最后一页(有23个数据,per_page=10,和orphans=3,第一页10个数据,第二页13个数据)
- (可选):是否允许第一页为空。False且object_list为空,则会引发EmptyPage错误
其方法中有:
函数 | 描述 |
---|---|
Paginator.get_page | 返回Page具有给定从 1 开始的索引的对象,同时还处理超出范围和无效页码 |
Paginator.page(数字) | 返回Page具有给定从 1 开始的索引的对象 |
其属性有如下三个:
- Paginator.count 数据总量
- Paginator.num_pages 总页数
- Paginator.page_range 页码迭代器
1.2 Page类
一般不会用page来构建对象,使用Paginator.page(下面的实战代码中也有讲到)
函数 | 描述 |
---|---|
Page.has_next () | 有下一页,返回true |
Page.has_previous () | 有上一页,返回true |
Page.has_other_pages () | 有上下页,返回true |
Page.next_page_number () | 返回下个页号,如果下页不存在,引发InvalidPage异常 |
Page.previous_page_number () | 返回上个页号,如果上页不存在,引发InvalidPage异常 |
Page.start_index() | 返回页面上第一个对象的从 1 开始的索引 |
Page.end_index() | 返回页面上最后一个对象的从 1 开始的索引 |
其属性有:
- Page.object_list 此页面上的对象列表
- Page.number 此页面的从 1 开始的页码
- Page.paginator 关联的Paginator对象
处理分页时候的异常一般有如下:
异常信息 | 描述 |
---|---|
异常InvalidPage | 向分页器传递无效页码时引发的异常的基类 |
异常PageNotAnInteger | page不是一个整数数值。可通过except InvalidPage |
异常EmptyPage | page为有效数值,但该页面上不存在对象引发。可通过except InvalidPage |
2. 实战讲解
对于函数中的**kwargs解释,可看我这篇文章:Python关于 *args 和 **kwargs 参数的详解(全)
结合form表单进行获取数据
@require_GET
@login_required
# 省略详细内容注解 @form_check
def AudioProject(request):
page_num = request.form.cleaned_data['page']
search_projname = request.form.cleaned_data.get('projname') or None
start_time, end_time = None, None
if date_range:
start_time, end_time = request.form.cleaned_data.get('range')
start_time = datetime.datetime.strptime(start_time, "%Y-%m-%d %H:%M")
end_time = datetime.datetime.strptime(end_time, "%Y-%m-%d %H:%M")
# 根据种类进行过滤查询
page = models.get_projects_by_page(page=page_num, projname=search_projname, start_time=start_time, end_time=end_time)
核心函数方法:
# 全局变量的setting PAGE_SIZE设置为15页
def get_projects_by_page(page=1, size=settings.PAGE_SIZE, **kwargs):
# 此处主要判断 value值为空,既将key也清空
# python web中的应用 和此处的Paginator 无直接关系,主要阐明其意思
for k, v in kwargs.items():
if v is None:
kwargs.pop(k)
# 调用该函数,通过获取数据源,该数据源可以使列表、元祖以及结果集等,
# 再代入size ,每页显示的个数
project_paginator = Paginator(get_search_projects(**kwargs), size)
# 优化显示分页,为了统一分页
project_page = get_page(project_paginator, page)
return project_page
统一的分页处理:
def get_page(paginator, page):
"""统一的分页管理."""
try:
page_datas = paginator.page(page)
except PageNotAnInteger:
page_datas = paginator.page(1)
except EmptyPage:
# Paginator.num_pages
page_datas = paginator.page(paginator.num_pages)
return page_datas
以下html 为 python template的模板
只讲解Paginator的核心代码
{% macro show_page(errors, page) %}
{% if not errors %}
<div class="btn-group">
<!-- 页码的基于 1 的范围迭代器-->
{% if page.paginator.page_range | length > 10 %}
<!-- 如果有前一页则返回-->
{% if page.has_previous() %}
<!-- value值返回上一个页码-->
<button type="button"
value="{{ page.previous_page_number() }}"
class="btn btn-white btn-page"><i
class="fa fa-chevron-left"></i></button>
{% endif %}
<!-- page.number为从页面1开始的页码-->
{% for page_idx in page.paginator.page_range[
((0, page.number - 5)|sort)[-1]: page.number + 4] %}
<button value="{{ page_idx }}"
class="btn btn-page btn-white {% if page_idx == page.number %}active{% endif %}">{{ page_idx }}</button>
{% endfor %}
<input type="text" class="form-control"
id="pageJumperInputID" style="width: 50px; display:
inline-block" value="{{ page.number }}">
<button class="btn btn-primary btn-page-jumper"
id="pageJumperButtonID"
style="float: inherit">跳转
(共{{ page
.paginator.num_pages }}页)
</button>
<!-- 如果有下一页-->
{% if page.has_next() %}
<!-- value值返回下一个页码-->
<button value="{{ page.next_page_number() }}" type="button"
class="btn btn-page btn-white"><i
class="fa fa-chevron-right"></i></button>
{% endif %}
{% else %}
<!-- 如果有前一页则返回-->
{% if page.has_previous() %}
<!-- value值返回上一个页码-->
<button type="button"
value="{{ page.previous_page_number() }}"
class="btn btn-white btn-page"><i
class="fa fa-chevron-left"></i></button>
{% endif %}
{% for page_idx in page.paginator.page_range %}
<button value="{{ page_idx }}"
class="btn btn-page btn-white {% if page_idx == page.number %}active{% endif %}">{{ page_idx }}</button>
{% endfor %}
{% if page.has_next() %}
<button value="{{ page.next_page_number() }}" type="button"
class="btn btn-page btn-white"><i
class="fa fa-chevron-right"></i></button>
{% endif %}
{% endif %}
</div>
{% endif %}
{% endmacro %}
分页的js代码模块:
{% macro show_page_js() %}
<script>
$(document).ready(function () {
$(".btn-page-jumper").bind("click", function (e) {
var page = $("#pageJumperInputID").val();
if (typeof (fliover) == "function") {
fliover(page);
} else {
const matchReg = /[&|?]page=.*/g;
const search_prefix = window.location.search.replace(matchReg, "");
if(search_prefix !== ""){
window.location.assign(search_prefix + "&page=" + page);
}else{
window.location.assign("?page=" + page);
}
}
});
$(".btn-page").bind("click", function (e) {
var page = this.value;
if (typeof (fliover) == "function") {
fliover(page);
} else {
const matchReg = /[&|?]page=.*/g;
const search_prefix = window.location.search.replace(matchReg, '')
if(search_prefix !== ""){
window.location.assign(search_prefix + "&page=" + page);
}else{
window.location.assign("?page=" + page);
}
}
});
var input = document.getElementById("pageJumperInputID");
if (input != undefined) {
input.addEventListener("keyup", function (event) {
// Cancel the default action, if needed
event.preventDefault();
// Number 13 is the "Enter" key on the keyboard
if (event.keyCode === 13) {
// Trigger the button element with a click
document.getElementById("pageJumperButtonID").click();
}
});
}
});
</script>
{% endmacro %}