ページネーション¶
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 をビューで使用する¶
次は、ビューの中で queryset をページネーションするために 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})
In the template list.html, you'll want to include navigation between
pages along with any interesting information from the objects themselves:
{% 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">« 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 »</a>
{% endif %}
</span>
</div>
Paginator オブジェクト¶
Paginator には次のようなコンストラクタがあります。
Required arguments¶
object_listリスト、タプル、
QuerySet、あるいはその他のスライス可能なオブジェクトで、count()または__len__()を実装しているオブジェクトを指定します。一貫性のあるページネーションのためには、QuerySetは並び替えられていなければなりません。たとえば、order_by()句を使用したり、モデルでデフォルトのorderingを指定します。巨大な
QuerySetのページネーションによるパフォーマンス上の問題非常に多数のアイテムを持つ
QuerySetを使用した場合、数の大きいページをリクエストした時位に、データベースによっては速度が低下する場合があります。これは、OFFSET数を数えるために必要なLIMIT/OFFSETクエリの処理時間が、ページ数が増えるにつれて長くなってしまうためです。per_page- 1ページごとに含まれる最大のアイテム数です。これには孤立したアイテム (orphan) は含まれません (詳しくは以下の
orphansオプション引数をご覧ください)。
Optional arguments¶
orphans- 最後のページに少数のアイテムだけが表示されるのを防ぎたい時に使用します。もし最後のページに
orphansで指定した数以下のアイテムしか含まれない場合、最終ページではなく、1つ前のページにそれらのアイテムが追加されます (そのページが最終ページになります)。たとえば、23個のアイテムがあり、per_page=10とorphans=3をしていしたとします。その場合、ページ数は2になり、最初のページには10個のアイテムが、2つ目の (最後の) ページには13個のアイテムが表示されることになります。orphansはデフォルトで0になっているため、ページが合体することはなく、最後のページには1アイテムだけが表示されるかもしれません。 allow_empty_first_page- 最初のページがからであることを許すかどうかを指定します。もし
Falseを指定し、object_listが空だった場合、EmptyPageエラーが起こります。
メソッド¶
-
Paginator.get_page(number)[ソース]¶ 1から始まるインデックスをもつ
Pageオブジェクトを返します。このオブジェクトは、範囲外のページ数や無効なページ数もハンドリングします。与えられた値が数字でなかった場合は、最初のページを返します。ページ数が負の数や全体のページ数より大きかった場合は、最後のページを返します。
例外 (
EmptyPage) が発生するのは、Paginator(..., allow_empty_first_page=False)を指定して``object_list`` が空であるとき、かつそのときに限ります。
-
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 例外¶
リクエストされたページが無効な値だったり (たとえば、整数でなかったなど)、オブジェクトが1つも含まれていなかった場合、Paginator.page() メソッドは例外を起こします。一般には、InvalidPage 例外を捕捉すれば十分ですが、粒度を細かくしたい場合は、以下の例外のいずれかを捕捉することもできます。
2つの例外は InvalidPage のサブクラスであるため、両方とも単に except InvalidPage と書くだけでハンドリングできます。
Page オブジェクト¶
通常は Page オブジェクトを手動で作ることはありません。Paginator.page() メソッドで作成します。
-
class
Page(object_list, number, paginator)[ソース]¶ 1つのページは、
len()を使ったり直接イテレーションした時、Page.object_listのシーケンスのように振る舞います。
メソッド¶
-
Page.next_page_number()[ソース]¶ 次のページ数を返します。次のページが存在しないときは
InvalidPage例外を起こします。
-
Page.previous_page_number()[ソース]¶ 前のページ数を返します。前のページが存在しないときは
InvalidPage例外を起こします。
-
Page.start_index()[ソース]¶ ページ上の最初のオブジェクトに対する、1から始まるインデックスを返します。これは、ページネータのリストに含まれる全オブジェクトに対するインデックスです。たとえば、5個のオブジェクトのリストを各ページ2オブジェクトでページネーションしている場合、2ページ目の
start_index()は3を返すでしょう。
-
Page.end_index()[ソース]¶ ページ上の最後のオブジェクトに対する、1から始まるインデックスを返します。これは、ページネータのリストに含まれる全オブジェクトに対するインデックスです。たとえば、5個のオブジェクトのリストを各ページ2オブジェクトでページネーションしている場合、2ページ目の
end_index()は4を返すでしょう。