ペジネータ (paginator)

revision-up-to:17812 (1.4)

Django は、ページ分割された (paginated) データを扱うためのペジネータ (paginator) クラスを提供しています。ページ分割とは、データが複数のページに わたって表示され、それぞれのページに「前へ/次へ」といったリンクがある状態 を指します。ペジネータのクラスは、 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
>>> p.page_range
[1, 2]

>>> 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()
3
>>> 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

Note

Paginator にはリストやタプル、 Django の QuerySet の他に、 count()__len__() をメソッドを備えた任意のオブジェクトを渡せ ることに注意してください。 Paginator は、渡されたコンテナ内のオブジェ クトの数を調べるのに、まず count() を呼び出そうとし、 count() がなければ len() にフォールバックします。これにより、 QuerySet のようなオブジェクトで、より効率的な count() を呼び出しています。

Paginator をビューで使う

今度は、ビューの中でクエリセットをページ分割するために Paginator を 使うもう少し複雑な例です。どのように結果が表示されるかが分かるように、ビュー とビューが使うテンプレートの両方を示します。この例では Contacts モデルが 既にインポートされているものとします。

ビュー関数は以下のようになります:

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

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

    page = request.GET.get('page')
    try:
        contacts = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer, deliver first page.
        contacts = paginator.page(1)
    except EmptyPage:
        # If page is out of range (e.g. 9999), deliver last page of results.
        contacts = paginator.page(paginator.num_pages)

    return render_to_response('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>
Django 1.4 で変更されました: 以前は Page オブジェクトがイテレーション可能ではなかったので {% for contact in contacts.object_list %} とする必要がありました。

Paginator オブジェクト

Paginator クラスは、以下のようなコンストラクタを持っています:

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

必須の引数

object_list
リスト、タプル、Django の QuerySet, または count()__len__() メソッドを備えたオブジェクトを指定します。
per_page
一ページに表示するオブジェクトの最大個数です (後述のオプション引数 orphans も参照してください)。

オプションの引数

orphans
最後のページに表示する要素の個数です。デフォルト値はゼロに設定されてい ます。最後のページに表示するオブジェクトの個数を一定以上にしたい場合に 使ってください。 orphans を設定すると、ページ分割した要素の末尾のグ ループの個数が orphans の設定値以下のときに、そのグループの要素だけ のページを表示するのではなく、ひとつ前のページの要素に繰り入れて表示し ます(すなわち、一つ手前のページが最終ページになります)。例えば、 per-page=10 かつ orphans=3 のペジネータで 23 個のオブジェクトを 表示すると、ページは二つに分割され、最初のページには 10 個、二つ目のペー ジ(最終ページ)には 13 個の要素を表示します。
allow_empty_first_page
先頭のページが空でもよいかどうかを決めるパラメタです。 False に設定 すると、 object_list が空のときに EmptyPage 例外を送出します。

メソッド

Paginator.page(number)

指定ページの Page オブジェクトを返します。ページ番号は 1 から 始まる数です。存在しないページを指定すると InvalidPage を送出し ます。

属性

Paginator.count

全てのページにわたるオブジェクトの合計数です。

Note

object_list に入っているオブジェクトの個数を調べるときに、 Paginator はまず object_list.count() を呼びます。 object_listcount() メソッドがなければ、 Paginatorlen(object_list) に切り替えます。これにより、 QuerySet のようなオブジェクトで、より効率的な count() を呼 び出しているのです。

Paginator.num_pages

総ページ数です。

Paginator.page_range

[1, 2, 3, 4] のように、 1 から始まるページ番号です。

InvalidPage 例外

exception InvalidPage

ペジネータが不正なページ番号を渡された時に送出される例外の基底クラス です。

リクエストされたページが無効なページ (整数でないページ番号) であったり、ペー ジにオブジェクトが含まれていない場合、 Paginator.page() メソッドは例外 を送出します。通常はこの InvalidPage 例外をトラップするだけですみますが、 より細かく例外処理するには、以下の例外をトラップしてください:

exception PageNotAnInteger

page() に整数でない値を渡したときに送出されます。

exception EmptyPage

page() には有効な値が渡されているが、ページにオブジェクトが含まれな い場合に送出されます。

どちらの例外も :exc:InvalidPage のサブクラスなので、単に except InvalidPage としておけば両方の例外を捕捉できます。

Page オブジェクト

普通は Page オブジェクトを手動で作ろうとは思わないでしょう。 Paginator.page() を使って取得できます。

Page(object_list, number, paginator):
Django 1.4 で新たに登場しました: len() を使ったり直接イテレーションする場合、ページは Page.object_list のように振る舞います。

メソッド

Page.has_next()

次のページがある場合に True を返します。

Page.has_previous()

前のページがある場合に True を返します。

Page.has_other_pages()

前後 いずれか のページがある場合に True を返します。

Page.next_page_number()

次のページが存在するかどうかに関係なく、「次のページ」のページ番号を返 します。

Page.previous_page_number()

前のページが存在するかどうかに関係なく、「前のページ」のページ番号を返 します。

Page.start_index()

ページの先頭のオブジェクトの通番を、ペジネータ全体にわたるオブジェクト のリストの通番で返します。通番は 1 から始まります。例えば、ページあたり 2 個のオブジェクトを表示するペジネータで 5 個のオブジェクトをページ分割 している場合、2 ページ目の start_index()3 を返します。

Page.end_index()

ページの末尾のオブジェクトの通番を、ペジネータ全体にわたるオブジェクト のリストの通番で返します。通番は 1 から始まります。例えば、ページあたり 2 個のオブジェクトを表示するペジネータで 5 個のオブジェクトをページ分割 している場合、2 ページ目の end_index()4 を返します。

属性

Page.object_list

ページ上のオブジェクトのリストです。

Page.number

1 から数えたページ番号です。

Page.paginator

ページの結びついている Paginator オブジェクトです。