【Django】Paginatorを継承してシンプルなページ付けの実装


投稿日 2019年7月1日 >> 更新日 2023年3月2日

今回はDjangoのクラスであるPaginatorを継承してTODOアプリのページ付けを実装していきたいと思います。

ここではシンプルなページ付けを実装するので、より複雑な処理を行うページ付けに関しては「django paginator」と検索し深堀してください。

TODOアプリに関しては過去に実装したものを使用します。MVTのソースコードを掲載しておきますが詳しく知りたい場合は以下のリンクに飛んでください。

実行環境&使用ライブラリ

実行環境
Windows Subsystem for Linux
Python 3.6.8
pip 9.0.1
使用ライブラリ ライセンス
Django==2.2.6 BSD

TODOアプリのソースコード

# app/todo/urls.py

from django.urls import path
from . import views


app_name = 'todo'

urlpatterns = [
        path('', views.index, name='index'),
        path('todo/<str:category>/', views.todo_category, name='todo_category'),
]
# app/todo/models.py

from django.db import models


class Category(models.Model):
    title = models.CharField('カテゴリー', max_length=20)

    def __str__(self):
        return self.title


class Todo(models.Model):
    title = models.CharField('タイトル', max_length=50)
    created_at = models.DateTimeField(auto_now_add=True)
    category = models.ForeignKey(Category, on_delete=models.PROTECT)

    def __str__(self):
        return self.title
# app/todo/views.py

from django.shortcuts import render
from .models import Todo, Category


def index(request):
    todo = Todo.objects.order_by('title')
    return render(request, 'todo/index.html', {'todo': todo})


def todo_category(request, category):
    category = Category.objects.get(title=category)
    todo = Todo.objects.filter(category=category).order_by('title')
    return render(request, 'todo/index.html', {'todo': todo, 'category': category })
<!-- app/todo/templates/todo/base.html -->

<!doctype html>
<html>
    <head>

        <title>TODO</title>

    </head>
    <body>

        <h2>カテゴリー</h2>
        <ul>
                <a href="{% url 'todo:index' %}">
            <li>トップ</li>
        </a>
        {% for category in category_list %}
            <a href="{% url 'todo:todo_category' category %}">
                <li>{{ category }}</li>
            </a>
        {% endfor %}
        </ul>
        {% block content %}
        {% endblock %}

    </body>
</html>
<!-- app/todo/templates/todo/index.html -->

{% extends 'todo/base.html' %}

{% block content %}

    <h2>TODOリスト</h2>

    {% if category %}
        <div>
            <h2 >カテゴリー: {{ category.title }}</h2>
        </div>
    {% endif %}

    {% for todos in todo %}
        <ul>
            <li>{{ todos.title }}--{{ todos.created_at }}</li>
        </ul>
    {% endfor %}

{% endblock %}

views.pyにてPaginatorを継承する

Paginatorを使うためにはDjango側でクラス定義されているfrom django.core.paginator import Paginatorという要素をインポートし一覧表示など実装している関数に渡します。

# app/todo/views.py

from django.shortcuts import render
from django.core.paginator import Paginator  # ←ここ
from .models import Todo, Category


def index(request):
    todo = Todo.objects.order_by('title')

    """TODOリストでは3つずつ表示させる"""
    paginator = Paginator(todo, 3)
    page = request.GET.get('page')
    todo = paginator.get_page(page)
    return render(request, 'todo/index.html', {'todo': todo})


def todo_category(request, category):
    category = Category.objects.get(title=category)
    todo = Todo.objects.filter(category=category).order_by('title')

    """カテゴリを選択したら1つずつ表示させる"""
    paginator = Paginator(todo, 1)
    page = request.GET.get('page')
    todo = paginator.get_page(page)
    return render(request, 'todo/index.html', {'todo': todo, 'category': category })

一覧表示用とカテゴリ一覧用とそれぞれ分けてページ付けを行っています。

テンプレートでの記述

テンプレートでは新しくpage.htmlを作成し、作成したpage.htmlをincludeテンプレートタグとしてindex.html
に渡します。

<!-- app/todo/templates/todo/page.html -->

<div class="pagination">
    <span class="step-links">
        {% if todo.has_previous %}
            <a href="?page=1">&amp;laquo;</a>
            <a href="?page={{ todo.previous_page_number }}">前へ</a>
    {% endif %}
    <span class="current">
        {{ todo.number }} / {{ todo.paginator.num_pages }}
     </span>
     {% if todo.has_next %}
         <a href="?page={{ todo.next_page_number }}">次へ</a>
             <a href="?page={{ todo.paginator.num_pages }}">&raquo;</a>
     {% endif %}
    </span>
</div>
<!-- app/todo/templates/todo/index.html -->

{% extends 'todo/base.html' %}

{% block content %}

    <h2>TODOリスト</h2>

    {% if category %}
        <div>
            <h2 >カテゴリー: {{ category.title }}</h2>
        </div>
    {% endif %}

    {% for todos in todo %}
        <ul>
            <li>{{ todos.title }}--{{ todos.created_at }}</li>
        </ul>
    {% endfor %}

    <!-- ここ -->
    {% include 'todo/page.html' %}

{% endblock %}

それでは確認をしてみましょう。

カテゴリ一覧

しっかりページが遷移されれば成功です。

シンプルなページ付けの実装となりましたが、見えないページ一覧を減らすことでメモリ使用量の削減にも繋がると思うので無駄ではないですね。

それでは以上となります。

ご覧いただきありがとうございました。