ページネーションとは、日本語でいうと「ページ送り」です。
例えば、ブログ記事が100件あったとします。
これを1ページに100件ではなく、10件ずつ10ページに分割することができます。
この分割機能の事を「ページネーション」といいます。
本稿ではページネーションについて解説します。
クラスベースビューで実行するページネーション
実行可能なクラスベースビュー
クラスベースビューでは簡単にページネーションを実施できるようになっています。
はじめから組み込まれているということですね。
ページネーションできるのは、テンプレートで「object_list
」が使用可能なViewです。
具体的には、MultipleObjectMixin
で実装されているため、継承しているクラスであれば使用可能です。
ListView
・ArchiveView
・YearArchiveView
・MonthArchiveView
・WeekArchiveView
・DayArchiveView
・TodayArchiveView
といったところでしょうか。
これらのViewであれば、クラス変数を設定することで自動的にページネーションが使用可能になります。
逆の言い方をすれば、クラス変数が設定されていないときはページネーションはOFFになっています。
設定する変数
例として、ListViewで記述します。
from django.views.generic import ListView
from .models import Article
class ArticleListView(models.ListView):
model = Article
paginated_by = 10 # この行を追加
このようにpaginated_by
を追加するとページネーションがONになり、テンプレートに変数を通してデータが供給されます。
記述がない場合は、変数は供給されますが、None
となってデータは供給されません。
供給される変数とデータ
供給されるデータは以下のとおりです。
* is_paginated
* page_obj
* paginator
この3つのデータでページネーションを実装できます。
実装はテンプレートで行います。Viewにページネーション特有の処理を実装する必要はありません。
ドキュメントを見ればわかりますが、paginator
はpage_obj
にも内包されているため、page_obj
から(page_obj.paginator
のように)使用できます。
それぞれの変数がどのような役割を持っているかも説明します。
is_pagenated
bool型です。
ページネーションがONになっていればTrue、OFFならFalseになります。
page_obj
オブジェクトです。
そのページの情報です。
現在何ページ目なのか、前のページはあるのか、次のページがあるのかなどが取得できるオブジェクトです。
paginator
オブジェクトです。
ページ分割に関する情報が入っています。
全部で何ページあるのか、記事の数はいくつなのか、分割されるモデルのリスト(要はobject_list
)などが入っています。
ページネーションを個別ページで実装する
MultipleObjectMixin
を実装しているViewであればページネーションはすでに実装済みであることはわかりました。
ですが、プロジェクトによってはDetailViewのような個別のページでもページネーションしたいことがあります。
たとえば、「カテゴリの個別ページの中に記事の一覧を表示したいが、記事が沢山あるのでページネーションしたい」といった具合です。
このような場合、クラスベースビューであればDetailViewでの実装になるため、独自でページネーションの実装が必要です。
from django.views.generic import DetailView
from django.core import paginator
from .models import Category
class CategoryDetailView(DetailView):
model = Category
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
# ... いろいろな実装
category_list = context['object_list']
paginated_by = 10 # 分割数
page = self.request.GET.get("page")
my_paginator = paginator.Paginator(category_list, paginated_by)
try:
page_obj = my_paginator.page(page)
except (paginator.PageNotAnInteger, paginator.EmptyPage):
page_obj = my_paginator.page(1)
context['is_paginated'] = True
context['page_obj'] = page_obj
context['paginator'] = my_paginator
context['object_list'] = page_obj.object_list
return context
コードを読むと大体理解できると思います。
paginator
オブジェクトを作成するpaginator
オブジェクトからページ番号を使ってpage_obj
を生成する- テンプレートで使う
といった流れです。
テンプレートで使用する
サンプルコードはDjangoのドキュメントから引用します。
<div class="pagination">
<span class="step-links">
{% if page_obj.has_previous %}
<a href="?page=1">« first</a>
<a href="?page={{ page_obj.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">next</a>
<a href="?page={{ page_obj.paginator.num_pages }}">last »</a>
{% endif %}
</span>
</div>
各変数について説明すれば動作の理解が進むと思います。
page_obj.has_previous
- 前のページがあればTrue、最初のページであればFalse
page_obj.previous_page_number
- 前のページが何ページ目であるか、数字で返す。今2ページ目であれば1になる。
page_obj.next_page_number
- 次のページが何ページ目であるか、数字で返す。今2ページ目であれば3になる。
page_obj.has_next
- 次のページがあればTrue、最後のページであればFalse
page_obj.number
- 現在のページ番号を返す。
page_obj.paginator.num_pages
- 全体のページ数を返す。
このようになっています。
{% for page in paginator.page_range %}
{{ page }}
{% endfor %}
このようにすれば、page
にページ番号が代入されてループが回ります。
ループはページ番号のループであることに注意してください。
このとき、{% if page_obj.number == page %}
などとすると、ページ番号のループの中でアクセスされたページかどうかを判定できます。
※ 自分が何ページ目にアクセスしているかが判定できる
それを使えば、ページ番号をハイライトしたりできますね。
おっと、ここまでDjangoがページをどのように特定しているのかを解説していませんでした。
ページネーションを実行すると、URLにクエリパラメーターが追加されます。
追加されるクエリパラメーターはpage=N
というもので、N
には整数が入ります。もちろんこれがページ番号です。page=3
だとすれば、3ページ目にアクセスしていることになり、3ページ目のデータが表示されます。
逆の言い方をすれば、テンプレートでリンクを作成する際はこのpage=
を忘れるとページネーションが成り立たなくなります。
前述した例にも記載されていますが、{% url 'article:index' %}?page={{ page_obj.next_page_number }}
のようにしてリンクを作成しなければなりません。
注意してください。
まとめ
この記事では、ページネーションのやり方について解説しました。
クラスベースビューでは、paginated_by
を追加するだけでpagination
がONになります。
また、カテゴリの個別ページにカテゴリに所属する記事の一覧をページネーションする場合についても解説しました。
自分でpaginator
を使ってオブジェクトを作成することでページネーションを実装できます。
思ったよりも簡単に実装できることに驚くと思います。
正直、Railsのkaminari
などに比べるとちょっと面倒な部分もあるのですが、自分で細かく実装できるという良い面もあります。
うまく活用してください。