Django提供了一些类来帮助你管理分页的数据 — 也就是说,数据被分在不同页面中,并带有“上一页/下一页”链接。 这些类位于django/core/paginator.py
中。
向Paginator
提供对象的一个列表,以及你想为每一页分配的元素数量,它就会为你提供访问每一页上对象的方法:
>>> from django.core.paginator import Paginator
>>> objects = ['john', 'paul', 'george', 'ringo']
>>> p = Paginator(objects, 2)
>>> p.count
4
>>> p.num_pages
2
>>> type(p.page_range) # `<type 'rangeiterator'>` in Python 2.
<class 'range_iterator'>
>>> p.page_range
range(1, 3)
>>> page1 = p.page(1)
>>> page1
<Page 1 of 2>
>>> page1.object_list
['john', 'paul']
>>> page2 = p.page(2)
>>> page2.object_list
['george', 'ringo']
>>> page2.has_next()
False
>>> page2.has_previous()
True
>>> page2.has_other_pages()
True
>>> page2.next_page_number()
Traceback (most recent call last):
...
EmptyPage: That page contains no results
>>> page2.previous_page_number()
1
>>> page2.start_index() # The 1-based index of the first item on this page
3
>>> page2.end_index() # The 1-based index of the last item on this page
4
>>> p.page(0)
Traceback (most recent call last):
...
EmptyPage: That page number is less than 1
>>> p.page(3)
Traceback (most recent call last):
...
EmptyPage: That page contains no results
注
注意你可以向Paginator()
提供一个列表、一个元组、一个Django的QuerySet
或者任何带有count()
或__len__()
方法的对象。 当决定传入的对象所含对象的数量时,Paginator
会首先尝试调用count()
,接着如果传入的对象没有count()
方法则回退调用len()
。 这样会使类似于Django的QuerySet
的对象使用更加高效的count()
方法,如果存在的话。
Paginator
¶下面是一个有点复杂的例子,它们在视图中使用Paginator
来为查询集分页。 我们提供视图以及相关的模板来展示如何展示这些结果。 这个例子假设你拥有一个已经导入的Contacts
模型。
视图函数看起来像是这样:
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render
def listing(request):
contact_list = Contacts.objects.all()
paginator = Paginator(contact_list, 25) # 每页显示25个联系人
page = request.GET.get('page')
try:
contacts = paginator.page(page)
except PageNotAnInteger:
# 如果page不是一个整数,则展示第一页。
contacts = paginator.page(1)
except EmptyPage:
# 如果page不在范围内(例如,9999),则展示结果的最后一页。
contacts = paginator.page(paginator.num_pages)
return render(request, 'list.html', {'contacts': contacts})
在list.html
模板中,你会想要包含页面之间的导航,以及来自对象本身的有用信息:
{% for contact in contacts %}
{# Each "contact" is a Contact model object. #}
{{ contact.full_name|upper }}<br />
...
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if contacts.has_previous %}
<a href="?page={{ contacts.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Page {{ contacts.number }} of {{ contacts.paginator.num_pages }}.
</span>
{% if contacts.has_next %}
<a href="?page={{ contacts.next_page_number }}">next</a>
{% endif %}
</span>
</div>
Paginator
对象¶Paginator
类拥有以下构造函数:
object_list
列表、元组、QuerySet
或具有count()
或__len__()
方法的其它可切片的对象。 为了得到一致的分页,QuerySet
应该排好序,例如使用order_by()
子句或模型上的默认ordering
。
超大QuerySet
分页的性能问题
如果你用到的QuerySet
的元素数目非常巨大,请求后面页面的数据在某些数据库上可能会变慢,因为LIMIT
/OFFSET
查询需要计算OFFSET
记录的个数,当页面数往后的时候这需要更长的时间。
per_page
orphans
参数)。orphans
orphans
的项目,那么这些项目将被添加到上一页(成为最后一页),而不是让它们自己单独留在一个页面。 例如有23个记录,per_page=10
且orphans=3
时,将有两个页面;第一页有10个记录,第二页(最后一页)有13个记录。 orphans
默认为零,这意味着页面从不组合,最后一页可能有一个项目。allow_empty_first_page
False
且object_list
为空,则将引发一个EmptyPage
错误。Paginator.
page
(number)[source]¶返回在提供的下标处的Page
对象,下标以1开始。 如果提供的页码不存在,抛出InvalidPage
异常。
InvalidPage
异常¶Paginator.page()
放回在所请求的页面无效(比如不是一个整数)时,或者不包含任何对象时抛出异常。 通常,捕获InvalidPage
异常就够了,但是如果你想更加精细一些,可以捕获以下两个异常之一:
这两个异常都是InvalidPage
的子类,所以你可以通过简单的except InvalidPage
来处理它们。
Page
对象¶你通常不需要手动构建 Page
对象 -- 你可以从Paginator.page()
来获得它们。
Page
(object_list, number, paginator)[source]¶当调用len()
或者直接迭代一个页面的时候,它的行为类似于 Page.object_list
的序列。
Page.
next_page_number
()[source]¶返回下一页的页码。 如果下一页不存在,抛出InvalidPage
异常。
Page.
previous_page_number
()[source]¶返回上一页的页码。 如果上一页不存在,抛出InvalidPage
异常。
Page.
start_index
()[source]¶返回当前页上的第一个对象,相对于分页列表的所有对象的序号,从1开始。 比如,将五个对象的列表分为每页两个对象,第二页的start_index()
会返回3
。
Page.
end_index
()[source]¶返回当前页上的最后一个对象,相对于分页列表的所有对象的序号,从1开始。 比如,将五个对象的列表分为每页两个对象,第二页的end_index()
会返回 4
。
2017年9月6日