Django之分页器组件

Django提供了一个新的类来帮助你管理分页数据,这个模块存放在django.core.paginator.py。其中有两个核心类,一个是Paginator类,另一个是Page类。

Paginator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Paginator:

def __init__(self, object_list, per_page, orphans=0,allow_empty_first_page=True):
'''
初始化操作
:param object_list:可以是列表,元组,查询集或其他含有 count() 或 len()方法的可切片对象。对于连续的分页,查询集应该有序,例如有order_by()项或默认ordering参数。
:param per_page:每一页中包含条目数目的最大值,不包括独立成页的那页。(见下面 orphans参数解释)。
:param orphans:当你使用此参数时说明你不希望最后一页只有很少的条目。如果最后一页的条目数少于等于orphans的值,则这些条目会被归并到上一页中(此时的上一页变为最后一页)。
:param allow_empty_first_page:默认允许第一页为空。
'''
self.object_list = object_list
self._check_object_list_is_ordered()
self.per_page = int(per_page)
self.orphans = int(orphans)
self.allow_empty_first_page = allow_empty_first_page

类属性

  • Paginator.count:所有页面对象总数,即统计object_listitem数目。
    当计算object_list所含对象的数量时, Paginator会首先尝试调用object_list.count()
    如果object_list没有 count() 方法Paginator 接着会回退使用len(object_list)
  • Pagnator.num_pages:页面总数。
  • pagiator.page_range:页面范围,从1开始,例如[1,2,3,4]。

类方法

  • Paginator.page(number):根据参数number返回一个Page对象。(number为1的倍数)即:下面的Page类对象

Page

1
2
3
4
5
6
7
8
9
10
11
12
class Page(collections.abc.Sequence):

def __init__(self, object_list, number, paginator):
'''
初始化
:param object_list:当前页上所有对象的列表。
:param number:当前页的序号,从1开始。
:param paginator:相关的Paginator对象。
'''
self.object_list = object_list
self.number = number
self.paginator = paginator

类方法

  • Page.has_next() 如果有下一页,则返回True
  • Page.has_previous() 如果有上一页,返回 True
  • Page.has_other_pages() 如果有上一页或下一页,返回True
  • Page.next_page_number() 返回下一页的页码。如果下一页不存在,抛出InvlidPage异常。
  • Page.previous_page_number() 返回上一页的页码。如果上一页不存在,抛出InvalidPage异常。
  • Page.start_index() 返回当前页上的第一个对象,相对于分页列表的所有对象的序号,从1开始。
    比如,将五个对象的列表分为每页两个对象,第二页的start_index()会返回3。
  • Page.end_index() 返回当前页上的最后一个对象,相对于分页列表的所有对象的序号,从1开始。
    比如,将五个对象的列表分为每页两个对象,第二页的end_index() 会返回 4。

异常处理

  • InvalidPage(Exception): 异常的基类,当paginator传入一个无效的页码时抛出。
    Paginator.page()放回在所请求的页面无效(比如不是一个整数)时,或者不包含任何对象时抛出异常。通常,捕获InvalidPage异常就够了,但是如果你想更加精细一些,可以捕获以下两个异常之一:
  • exception PageNotAnInteger,当向page()提供一个不是整数的值时抛出。
    exception EmptyPage,当向page()提供一个有效值,但是那个页面上没有任何对象时抛出。
    这两个异常都是InalidPage的子类,所以可以通过简单的except InvalidPage来处理它们。

示例

视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from django.shortcuts import render
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

from .models import Article

def index(request):
# 获取所有文章queryset
articles = Article.objects.all()
# pageinator = Paginator(object_list=articles, per_page=4, orphans=2, allow_empty_first_page=True)
# 位置参数,等同于上方关键字参数,每页显示4条
pageinator = Paginator(articles, 4)
# 获取用户访问的页数
page = request.GET.get('page')
try:
# 获取当前访问页的Page对象
article_datas = pageinator.page(page)
except PageNotAnInteger:
# 页码不是整数时传送到第1页
article_datas = pageinator.page(1)
except EmptyPage:
# 页码不存在时,跳转到最后一页
article_datas = pageinator.page(pageinator.num_pages)

return render(request, 'index.html', {'articles':article_datas})

模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<ul>
{% for arc in articles %}
<li>{{ arc.title }}</li>
{% endfor %}
</ul>
{% if articles.has_previous %}
<a href="?page={{ articles.previous_page_number }}">上一页</a>
{% endif %}
<em>页码:{{ articles.number }} of {{ articles.paginator.num_pages }}</em>
{% if articles.has_next %}
<a href="?page={{ articles.next_page_number }}">下一页</a>
{% endif %}
</body>
</html>

效果

img

进阶

我们稍加改动,让其显示所有页码

视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from django.shortcuts import render
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

from .models import Article

def index(request):
# 获取所有文章queryset
articles = Article.objects.all()
# pageinator = Paginator(object_list=articles, per_page=4, orphans=2, allow_empty_first_page=True)
# 位置参数,等同于上方关键字参数,每页显示4条
pageinator = Paginator(articles, 4)
# 获取用户访问的页数
page = request.GET.get('page')
# 获取当前访问页的Page对象
try:
article_datas = pageinator.page(page)
except PageNotAnInteger:
# 页码不是整数时传送到第1页
article_datas = pageinator.page(1)
except EmptyPage:
# 页码不存在时,跳转到最后一页
article_datas = pageinator.page(pageinator.num_pages)
# 传递页面Page对象和页码列表
return render(request, 'index.html', {'articles':article_datas, 'page_code':pageinator.page_range})

模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<ul>
{% for arc in articles %}
<li>{{ arc.title }}</li>
{% endfor %}
</ul>
{% if articles.has_previous %}
<a href="?page={{ articles.previous_page_number }}">上一页</a>
{% endif %}

<div>
{% for num in page_code %}
<a href="?page={{ num }}"{% if num == articles.number %} style="color: red"{% endif %}>{{ num }}</a>
{% endfor %}
</div>

<em>页码:{{ articles.number }} of {{ articles.paginator.num_pages }}</em>
{% if articles.has_next %}
<a href="?page={{ articles.next_page_number }}">下一页</a>
{% endif %}
</body>
</html>

效果

我们加上了页码,并高亮了当前页码。

img

扩展

我们当前文章较少,如果数量较多,如果页码达到100页,我们不可能将100个页码都显示在页面上,我们想要像百度一样,只显示10个页码,当前页码左右各5个要怎么做呢?我们数据量较少,我们这里只显示5个页码。

img

视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from django.shortcuts import render
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

from .models import Article

def index(request):
# 获取所有文章queryset
articles = Article.objects.all()
# pageinator = Paginator(object_list=articles, per_page=4, orphans=2, allow_empty_first_page=True)
# 位置参数,等同于上方关键字参数,每页显示4条
pageinator = Paginator(articles, 3)
# 获取用户访问的页数
page = request.GET.get('page')
# 获取当前访问页的Page对象
try:
article_datas = pageinator.page(page)
page = int(page)
except PageNotAnInteger:
# 页码不是整数时传送到第1页
article_datas = pageinator.page(1)
page = 1
except EmptyPage:
# 页码不存在时,跳转到最后一页
article_datas = pageinator.page(pageinator.num_pages)
page = pageinator.num_pages
# 处理页码
# 如果页码大于10则进行在处理,否则输出全部页码
if pageinator.num_pages>10:
# 如果当前页往前两个页码已经到页首,则显示1-5页码
if page-2 <1:
page_codes = range(1, 6)
# 如果当前页向后两个页码已经到页尾,则显示最后5个页码
elif page+2 > pageinator.num_pages:
page_codes = range(pageinator.num_pages-4, pageinator.num_pages+1)
# 否则显示当前页加前后两个页码
else:
page_codes = range(page-2, page+3)
else:
page_codes = pageinator.page_range
# 传递页面Page对象和页码列表
return render(request, 'index.html', {'articles':article_datas, 'page_code':page_codes})

模板

模板跟上面没有任何变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<ul>
{% for arc in articles %}
<li>{{ arc.title }}</li>
{% endfor %}
</ul>
{% if articles.has_previous %}
<a href="?page={{ articles.previous_page_number }}">上一页</a>
{% endif %}

<div>
{% for num in page_code %}
<a href="?page={{ num }}"{% if num == articles.number %} style="color: red"{% endif %}>{{ num }}</a>
{% endfor %}
</div>

<em>页码:{{ articles.number }} of {{ articles.paginator.num_pages }}</em>
{% if articles.has_next %}
<a href="?page={{ articles.next_page_number }}">下一页</a>
{% endif %}
</body>
</html>

效果

img