ページネーション¶
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 EmptyPage, PageNotAnInteger, 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)[ソース]¶ - New in Django 2.0.
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
を返すでしょう。