入門クラス

ページネーションの基本と書き方

ページネーションとは、日本語でいうと「ページ送り」です。

例えば、ブログ記事が100件あったとします。

これを1ページに100件ではなく、10件ずつ10ページに分割することができます。

この分割機能の事を「ページネーション」といいます。

本稿ではページネーションについて解説します。

クラスベースビューで実行するページネーション

実行可能なクラスベースビュー

クラスベースビューでは簡単にページネーションを実施できるようになっています。

はじめから組み込まれているということですね。

ページネーションできるのは、テンプレートで「object_list」が使用可能なViewです。

具体的には、MultipleObjectMixinで実装されているため、継承しているクラスであれば使用可能です。

ListViewArchiveViewYearArchiveViewMonthArchiveViewWeekArchiveViewDayArchiveViewTodayArchiveViewといったところでしょうか。

これらの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にページネーション特有の処理を実装する必要はありません。

ドキュメントを見ればわかりますが、paginatorpage_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

コードを読むと大体理解できると思います。

  1. paginatorオブジェクトを作成する
  2. paginatorオブジェクトからページ番号を使ってpage_objを生成する
  3. テンプレートで使う

といった流れです。

テンプレートで使用する

サンプルコードはDjangoのドキュメントから引用します。

<div class="pagination">
	<span class="step-links">
	{% if page_obj.has_previous %}
		<a href="?page=1">&laquo; 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 &raquo;</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などに比べるとちょっと面倒な部分もあるのですが、自分で細かく実装できるという良い面もあります。

うまく活用してください。

-入門クラス
-, ,