分页

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类拥有以下构造函数:

class Paginator(object_list, per_page, orphans=0, allow_empty_first_page=True)[source]

必选参数

object_list

列表、元组、QuerySet或具有count()__len__()方法的其它可切片的对象。 为了得到一致的分页,QuerySet应该排好序,例如使用order_by()子句或模型上的默认ordering

超大QuerySet分页的性能问题

如果你用到的QuerySet的元素数目非常巨大,请求后面页面的数据在某些数据库上可能会变慢,因为LIMIT/OFFSET查询需要计算OFFSET记录的个数,当页面数往后的时候这需要更长的时间。

per_page
每个页面上包含的元素的最大数目,不包含孤儿(参见下文的orphans参数)。

可选参数

orphans
当你不想要最后一个页面的记录很少时,请使用此选项。 如果最后一页通常会有一些小于或等于orphans的项目,那么这些项目将被添加到上一页(成为最后一页),而不是让它们自己单独留在一个页面。 例如有23个记录,per_page=10orphans=3时,将有两个页面;第一页有10个记录,第二页(最后一页)有13个记录。 orphans默认为零,这意味着页面从不组合,最后一页可能有一个项目。
allow_empty_first_page
第一页是否允许为空。 如果为Falseobject_list为空,则将引发一个EmptyPage错误。

方法

Paginator.page(number)[source]

返回在提供的下标处的Page对象,下标以1开始。 如果提供的页码不存在,抛出InvalidPage异常。

属性

Paginator.count

所有页面中包含的对象总数。

当确定object_list中包含的对象数量时,Paginator将首先尝试调用object_list.count() 如果object_list没有count() 方法,Paginator接着会回退使用len(object_list) 这样会使类似于Django’s QuerySet的对象使用更加便捷的count()方法,如果存在的话。

Paginator.num_pages

页面总数。

Paginator.page_range

页数的基于1的范围迭代器,例如产生[1, 2, 3, 4]

InvalidPage异常

exception InvalidPage[source]

异常的基类,当paginator传入一个无效的页码时抛出。

Paginator.page()放回在所请求的页面无效(比如不是一个整数)时,或者不包含任何对象时抛出异常。 通常,捕获InvalidPage异常就够了,但是如果你想更加精细一些,可以捕获以下两个异常之一:

exception PageNotAnInteger[source]

当向page()提供一个不是整数的值时抛出。

exception EmptyPage[source]

当向page()提供一个有效值,但是那个页面上没有任何对象时抛出。

这两个异常都是InvalidPage的子类,所以你可以通过简单的except InvalidPage来处理它们。

Page对象

你通常不需要手动构建 Page对象 -- 你可以从Paginator.page()来获得它们。

class Page(object_list, number, paginator)[source]

当调用len()或者直接迭代一个页面的时候,它的行为类似于 Page.object_list 的序列。

方法¶ T0>

Page.has_next()[source]

如果有下一页,则返回True

Page.has_previous()[source]

如果有上一页,返回 True

Page.has_other_pages()[source]

如果有上一页下一页,返回True

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

属性

Page.object_list

当前页上所有对象的列表。

Page.number

当前页的序号,从1开始。

Page.paginator

相关的Paginator对象。