1
4

More than 3 years have passed since last update.

【Django】ListViewの使い方とできること(2) ページネーション編

Posted at

はじめに

Djangoの標準ビューの一つでモデルを単純に利用した表示を行うListlViewが用意されています。
前回はListViewの基本的な機能をまとめましたが、触れていなかったページネーションについてまとめてみました。

ページネーションとは

多数のコンテンツを提供する際に、コンテンツを一定の数で区切ってページに分ける機能のことです。
(Google検索等でいう、結果が1ページ10件ずつ表示していくような感じのものです)

環境

  • Python 3.9
  • Django 3.2

モデル

本記事では、画面表示するモデルとして下記を使用することにします。

models.py
class Article(models.Model):
    title = models.CharField(max_length=128)
    content = models.TextField()
    is_published = models.BooleanField()

使い方

ページネーションの実装

ListViewではクラス変数として、pagenate_byに分割数を指定するだけでページネーションを実装することができます。

この変数を設定することで、GETパラメータ引数として与えられるページ番号をオフセットとして、表示対象のモデルオブジェクトが指定数で分割されてテンプレートへ渡されます。
テンプレートからはpage_obj変数(ページネーション管理オブジェクト)でページ状態を参照できるようになるため、これを利用してテンプレートにページ送り用のUI実装します。

モデルオブジェクトのobject_list には、分割後に該当のページ内で有効なオブジェクト(1ページ目であればソート順に1~5番目、2ページ目であれば、6~10番目...)のみにフィルタされた状態でテンプレートへ渡されます。

アクセス時のページ番号の指定には、GETパラメータpageにページ番号を渡す必要があります。リクエスト時に指定されていない場合は、1ページ目を指定したものとして扱われます。
テンプレート内で次ページへのリンク等を張る際にはリンク先URLのパラメータに目的のページの指定を行います。

views.py
from django.views.generic import ListView
from .models import Article

class ArticleListView(Listview):
    template_name = 'article_list.html'
    model = Article
    pagenate_by = 5 # モデルの分割数を追加
article_list.html
<!DOCTYPE html>
<html>
<head></head>
<body>
    <ul>
        <!-- object_list は該当ページ内で有効なオブジェクトのみにフィルタされて渡される-->
        {% for article in object_list %} 
            <li>{{ article.title }}</li>
        {% endfor %}
    </ul>

    <!-- ページネーション用のUI -->
    <div class="pagination">
        <span class="step-links">
            {% if page_obj.has_previous %}
                <!-- 現在よりも前のページが存在する場合 -->
                <a href="?page=1">&laquo; 先頭</a>
                <a href="?page={{ page_obj.previous_page_number }}">前へ</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 }}">次へ</a>
                <a href="?page={{ page_obj.paginator.num_pages }}">最後 &raquo;</a>
            {% endif %}
        </span>
    </div>
</body>
</html>

ページオブジェクトについて

ページネーションを有効化するとテンプレートへ自動で渡されるようになるページオブジェクト変数page_objで取得できる要素としては、下記のようなものがあります。

要素名 説明
has_next 現在より次のページが存在する場合True
has_previous 現在より前のページが存在する場合True
has_other_pages 現在とは他のページ(前or次)が存在する場合True
next_page_number 次のページ番号
previous_page_number 前のページ番号

ページネーション不要な際にUIを非表示にする

テンプレートへ自動で渡されるis_paginated変数でページネーションの有無(オブジェクト数が分割数以下(全1ページ)の場合false、2ページ以上になればtrue)を取得できます。
これを利用してページネーションが不要な場合にif文を利用して表示しない対象のコードを囲む等でUIの表示切り替えができます。

{% if is_paginated %}
    <!-- ページネーションのUIをここに書く -->
{% endif %}

はみ出し数の指定する

使いどころの難しい機能ですが、クラス変数としてpaginate_orphansを指定するとページネーションにより分割した際に、最後となるページで中途半端にはみ出してしまう場合に最終ページだけ例外的に分割数を超えて余剰分を表示できます。

例えば、総数が17個で分割数が5の場合、通常は4ページ構成(5-5-5-2)となりますが、はみ出し数を3とすると、最終ページで最大+3個まで分割数を超えることができるようになり、3ページ構成(5-5-7)となります。

views.py
from django.views.generic import ListView
from .models import Article

class ArticleListView(Listview):
    template_name = 'article_list.html'
    model = Article
    pagenate_by = 5 # モデルの分割数
    paginate_orphans = 3 # はみ出し数指定を追加

ページ番号のGETパラメータ名を変更する

ページ数指定の受け渡し時のGETパラメータ名としてpageを利用していましたが、これを任意の名前に変更することもできます。
パラメータ名を変更する場合は、クラス変数page_kwargに使用したいGETパラメータ名を指定します。

views.py
from django.views.generic import ListView
from .models import Article

class ArticleListView(Listview):
    template_name = 'article_list.html'
    model = Article
    pagenate_by = 5
    page_kwarg = 'result' # パラメータ名を変更

参考

Pagination | Django documentation | Django
https://docs.djangoproject.com/ja/3.2/topics/pagination/#paginating-a-listview

Page class | Django documentation | Django
https://docs.djangoproject.com/ja/3.2/ref/paginator/#page-class

1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4