分页 — Django 文档

来自菜鸟教程
Django/docs/2.2.x/topics/pagination
跳转至:导航、​搜索

分页

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)
<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
from django.shortcuts import render

def listing(request):
    contact_list = Contacts.objects.all()
    paginator = Paginator(contact_list, 25) # Show 25 contacts per page

    page = request.GET.get('page')
    contacts = paginator.get_page(page)
    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=1">&laquo; first</a>
            <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>
            <a href="?page={{ contacts.paginator.num_pages }}">last &raquo;</a>
        {% endif %}
    </span>
</div>

Paginator 物体

Paginator 类有这个构造函数:

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

必需参数

object_list

列表、元组、QuerySet 或其他具有 count()__len__() 方法的可切片对象。 为了一致的分页,应该订购 QuerySets,例如 使用 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.get_page(number)

返回具有给定的基于 1 的索引的 Page 对象,同时还处理超出范围和无效页码。

如果页面不是数字,则返回第一页。 如果页码为负数或大于页数,则返回最后一页。

仅当您指定 Paginator(..., allow_empty_first_page=False)object_list 为空时,它才会引发异常 (EmptyPage)。

Paginator.page(number)
返回具有给定的基于 1 的索引的 Page 对象。 如果给定的页码不存在,则引发 InvalidPage


属性

Paginator.count

跨所有页面的对象总数。

笔记

在确定 object_list 中包含的对象数量时,Paginator 将首先尝试调用 object_list.count()。 如果 object_list 没有 count() 方法,那么 Paginator 将回退到使用 len(object_list)。 这允许对象,例如 Django 的 QuerySet,在可用时使用更有效的 count() 方法。

Paginator.num_pages
总页数。
Paginator.page_range
页码的基于 1 的范围迭代器,例如 产生 [1, 2, 3, 4]


InvalidPage 异常

exception InvalidPage
当分页器传递无效页码时引发异常的基类。

Paginator.page() 如果请求的页面无效(即,不是整数)或不包含任何对象,则该方法会引发异常。 通常,捕获 InvalidPage 异常就足够了,但如果您想要更细化,则可以捕获以下任一异常:

exception PageNotAnInteger
page() 被赋予一个不是整数的值时引发。
exception EmptyPage
page() 被赋予有效值但该页面上不存在对象时引发。

这两个异常都是 InvalidPage 的子类,因此您可以使用简单的 except InvalidPage 处理它们。


Page 物体

您通常不会手动构建 Page 对象——您将使用 Paginator.page() 来获取它们。

class Page(object_list, number, paginator)
当使用 len() 或直接对其进行迭代时,页面的作用类似于 Page.object_list 的序列。

方法

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 开始的索引,相对于分页器列表中的所有对象。 例如,当用每页 2 个对象对 5 个对象的列表进行分页时,第二页的 start_index() 将返回 3
Page.end_index()
返回页面上最后一个对象的从 1 开始的索引,相对于分页器列表中的所有对象。 例如,当用每页 2 个对象对 5 个对象的列表进行分页时,第二页的 end_index() 将返回 4


属性

Page.object_list
此页面上的对象列表。
Page.number
此页面的基于 1 的页码。
Page.paginator
关联的 Paginator 对象。