Django Paginationで使える便利なhtmlのサンプルを紹介

DjangoのListViewクラスを使って一覧ページを作る時、htmlファイルにページネーションを作成する事が多いと思います。今回は私がよく使う使い勝手の良いページネーション用のhtmlコードを紹介します。

サンプルコード

以下がサンプルコードです。※Bootstrapclassを使用しています。

{% load core_tags %}
{% if page_obj.has_previous or page_obj.has_next %}
  <nav>
    <ul class="pagination">
      {% if page_obj.has_previous %}
        <li class="page-item">
          <a class="page-link" href='{{ request.path }}?page=1{{ request.GET.items|get_search_query_param }}'>
            <span>最初</span>
          </a>
        </li>
        <li class="page-item">
          <a class="page-link"
             href='{{ request.path }}?page={{ page_obj.previous_page_number }}{{ request.GET.items|get_search_query_param }}'
          >{{ page_obj.previous_page_number }}</a>
        </li>
      {% else %}
      <li class="page-item disabled">
        <a class="page-link">
          <span>最初</span>
        </a>
      </li>
      {% endif %}
      <li class="page-item active">
        <a class="page-link"
           href='{{ request.path }}?page={{ page_obj.number }}{{ request.GET.items|get_search_query_param }}'>{{ page_obj.number }}</a>
      </li>
      {% if page_obj.has_next %}
        <li class="page-item">
          <a class="page-link"
             href='{{ request.path }}?page={{ page_obj.next_page_number }}{{ request.GET.items|get_search_query_param }}'>{{ page_obj.next_page_number }}</a>
        </li>
        <li class="page-item">
          <a class="page-link" href='{{ request.path }}?page={{ paginator.num_pages }}{{ request.GET.items|get_search_query_param }}'>
            <span>最後</span>
          </a>
        </li>
      {% else %}
        <li class="page-item disabled">
          <a class="page-link">
            <span>最後</span>
          </a>
        </li>
      {% endif %}
    </ul>
  </nav>
{% endif %}

条件

このサンプルコードは以下の条件を満たすViewクラスを使用している必要があります。

  • ListViewクラスを継承している
  • paginate_byを定義している

読み込み方法

上記のコードをpagination.htmlとして保存します。ページネーションを追加したいhtmlファイルに、includeを使って以下のように読み込ませます。

{% include 'pagination.html' %}

‘pagination.html’部分はファイルパスに応じて適宜変更してください。

表示例

1ページしかない場合はページネーションを表示しません。
複数ページある場合は表示しているページにより表示が変わります。

1ページ目を表示している場合

bootstrapのページネーション。最初ページを表示している時。
1ページ目を表示

最初のページを表示している場合は’最初’の部分はクリックできないようにしています。該当コードを削除すれば非表示にすることもできます。

中間のページを表示している場合

bootstrapのページネーション。中間ページを表示している時。
中間のページを表示している場合

表示しているページの前後のページを表示しています。

最後のページを表示している場合

bootstrapのページネーション。最後ページを表示している時。
最後のページを表示している場合

1ページ目を表示している場合と同様です。

解説

{% load core_tags %}

独自実装したテンプレートタグを読み込んでいます。今回はファイル名をcore_tags.pyとします。

{% if page_obj.has_previous or page_obj.has_next %}

1ページのみか複数ページあるかどうかを判定しています。

<a class="page-link" href='{{ request.path }}?page={{ page_obj.previous_page_number }}{{ request.GET.items|get_search_query_param }}'>

{{ request.path }}

現在のURLを取得しています。

{{ page_obj.previous_page_number }}

前のページ数を取得しています。他のaタグでは後ろのページ数や最後のページ数を取得する変数を指定しています。

{{ request.GET.items|get_search_query_param }}

request.GET.itemsにはクエリパラメーターが辞書形式で定義されています。検索条件を指定できるページの場合、クエリパラメータを指定する必要がありあます。

クエリパラメーターの中には現在のページ数を表す’page’パラメーターがあります。’page’はListViewクラスで表示したいページ数を取得するためのキーとして定義されています。

page_kwarg = "page"

ページネーションで’page’パラメーターを個別で指定したいため、’page’パラメーターを除くクエリパラメーターを取得するためのテンプレートタグを定義する必要があります。

テンプレートタグを定義するcore_tags.pyに以下の関数を作成します。

@register.filter
def get_search_query_param(value):
    result = ''
    for k, v in value:
        if k != 'page':
            result += f'&{k}={v}'
    return result

処理速度を重視したい場合はViewクラス内で定義すると良いです(その場合はデコレーターは不要です)

応用編 表示数をクエリパラメーターで指定する

ユーザーが表示数を変更できるタイプのサイトはよくあります。

応用編としてデフォルトの表示数が10、指定可能な表示数が10~100のページに対応したページネーションを作成する方法を解説します。

表示数を動的に変更したい場合は次のようにget_paginate_by()をオーバーライドします。ここではpage_sizeというクエリパラメーターを使う想定です。

def get_paginate_by(self, queryset):
    page_size = self.request.GET.get('page_size', 10)
    try:
        return page_size if  10 <= int(page_size) <= 100 else 10
    except ValueError:
        return 10

この場合、テンプレートタグは次のようにpage_sizeも除外するように改変します。

@register.filter
def get_search_query_param(value):
    result = ''
    for k, v in value:
        if k not in ['page', 'page_size']:
            result += f'&{k}={v}'
    return result

aタグのhrefにpage_sizeのクエリパラメーターを追加します。{{paginator.per_page}}で現在の表示数を取得できます。

href='{{ request.path }}?page={{ page_obj.previous_page_number }}&page_size={{paginator.per_page}}{{ request.GET.items|get_search_query_param }}

まとめ

ListView関数やPaginationクラスは非常に便利ですが、ページネーション用のhtmlは独自実装する必要があります。

今回紹介したサンプルはどのURLでも対応しています。最初のページ、前後のページ、最後のページへのリンクの作成方法や、クエリパラメーターを保持する方法も紹介しているのでアレンジしやすいと思います。

スポンサーリンク
スポンサーリンク