【Django】クラスベースビュー(class-based view):ListViewを使用した一覧取得機能の実装

概要

クラスベースビュー(class-based view)で一覧検索機能を作成したのでまとめた。
一覧検索機能は、Djangoがもともと用意しているListViewクラスを継承してを作成する。

 

前提

Djangoの環境構築ができていること。

関連記事

概要 これからPythonのDjangoフレームワークを学ぶにあたり、開発環境を構築する。 今回はWindowsに仮想のLinuxOS環境を用意し、その中でPythonの仮想環境を作成するところまで行う。 LinuxOSを導入する[…]

また、Djangoの初期設定ができること。

関連記事

概要 Djangoフレームワークでアプリを作成するための基礎的な情報をまとめた。 プロジェクトとアプリの概念、Djangoの仕組み、DB作成方法や管理画面表示まわりについて扱う。   前提 環境構築ができていること。[…]

 

 

アプリ作成まで

Djangoプロジェクトの作成~アプリの作成まで行う。

各種設定方法については、「前提」の記事を参照すること。

Djangoプロジェクト作成

ターミナル


django-admin startproject todo_manager

 

Djangoアプリ作成

ターミナル


python3 manage.py startapp todoapps

テーブル

モデル概要

以下の関係性のモデルを作成する。

UserはデフォルトでDjangoがもっている利用者情報。
User(利用者テーブル)とTodo(タスクテーブル)は1対多の関係性。
TodoCategory(タスクカテゴリテーブル)とTodo(タスクテーブル)も1対多の関係性。

 

モデル作成

タスク一覧検索をするためのモデルを作成する。
todoapps/models.pyにTODOリストを管理するためのカテゴリとタスクテーブルとなるクラスを定義する。

todoapps/models.py


from django.db import models
from django.contrib.auth.models import User


class TodoCategory(models.Model):
    """TODOカテゴリテーブル"""

    category_name = models.CharField("カテゴリ名", max_length=255, null=False)

    class Meta:
        verbose_name = "TODOカテゴリ"
        verbose_name_plural = "TODOカテゴリ"

    def __str__(self):
        return self.category_name


class Todo(models.Model):
    """TODOタスクテーブル"""

    STATUS_CHOICES = [
        (0, "未完了"),
        (1, "完了"),
    ]

    task = models.CharField("タスク", max_length=255, null=False)
    memo = models.TextField("メモ", null=True, blank=True)
    status = models.IntegerField("ステータス", choices=STATUS_CHOICES, null=False)
    due_date = models.DateTimeField("期日", null=False)
    category = models.ForeignKey(
        TodoCategory, verbose_name="カテゴリID", on_delete=models.CASCADE
    )
    user = models.ForeignKey(User, verbose_name="利用者ID", on_delete=models.CASCADE)

    class Meta:
        verbose_name = "TODOタスク"
        verbose_name_plural = "TODOタスク"

    def __str__(self):
        return self.task

 

テーブル作成

以下のコマンドを実行してmodelsに定義したテーブルを作成する。

ターミナル


python3 manage.py makemigrations
python3 manage.py migrate todoapps

一覧表示用データ作成

管理者画面にログインし、カテゴリとタスクテーブルを登録する。

カテゴリ

▲管理者画面にて適当にタスクカテゴリを登録

 

タスク

管理画面でタスク追加

 

 

ListViewの実装

一覧表示機能をクラスベースで作成する。
Djangoに用意されているListViewを継承してクラスを作成することで、一覧表示ビュー機能を作成できる。

実装イメージ

▲ListViewを使用してTodoテーブルからタスク一覧を取得

 

ルーティング

todoapps/urls.py


from django.urls import path
from . import views


urlpatterns = [path("tasks/", views.TodoTopView.as_view(), name="top")]

サーバーを起動して「http://localhost:8000/tasks/」にアクセスすると、todoapps/views.pyのTodoTopViewクラスを呼ぶ。
ビューの名前をtopと定義している。

 

全検索

何の条件指定もなく、Todoテーブルの中身をすべて取得して画面に返却する方法。

サーバー側

todoapps/views.py


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


class TodoTopView(ListView):
    """ TODOリスト一覧表示 """

    model = models.Todo
    template_name = "todo/top.html"

ListViewをインポートして、継承したビュークラスを作成する。
これにより、ListViewクラスが保持する属性やメソッドを使用可能になる。

ListViewのmodel属性に、todoapps/models.pyに定義したTodoクラスを設定する。
ListViewのtemplate_name属性には、一覧表示するHTMLを指定する。

上記の記述を行うことで、以下の処理をListViewクラスが見えないところで実施してくれているイメージ。

・Todoテーブルから一覧データを全検索
・画面に表示するためのキーワード変数にデータを代入
・レスポンス返却

 

画面側

top.html


(略)
{% for todo in object_list %}
<tr>
  <td>{{ todo.category }}</td>
  <td>{{ todo.task }}</td>
  <td>{{ todo.due_date|date:"Y年m月d日" }}</td>
  <td>
    {% if todo.status == 0 %}
    未完了
    {% else %}
    完了
    {% endif %}
  </td>
</tr>
{% endfor %}
(略)
タスク一覧ループ

views.pyのListViewを継承したクラスにて、コンテキスト変数を定義していないとデフォルトで「object_list」に一覧データが代入される。

{% for todo in object_list %}

上記はTodoTopViewから返却されたタスク一覧をループしている処理。

尚、リストを画面でループさせる記述等についてはDjangoテンプレートと呼ばれる技術になる。

ループを表示させたり、画面表示させたりする単純な記述方法は、以下のJinjaテンプレートの技術と同じような記述方法になる。

関連記事

概要 Jinjaテンプレートを使用して、サーバーからの値をHTMLに埋め込む方法についてまとめた。 テンプレートとは、プログラムからデータを渡して動的に文章を作成するためのひながた。 Jinjaテンプレートでは、値を埋め込むだけで[…]

尚、コンテキスト変数を「object_list」以外に指定したい場合は以下のようにListViewの「context_object_name」を設定すればOK。

todoapps/views.py


class TodoTopView(ListView):
    """TODOリスト一覧表示"""

    model = models.Todo
    template_name = "todo/top.html"
    context_object_name = "tasks"

 

日付整形

due_dateは日時までテーブルに保持しているため、デフォルト表示だと「2023年7月29日0:00」と時間まで表示される。

日付までの表示にしたい場合、dateフィルタを使用して下記のように記述する。

<td>{{ todo.due_date|date:"Y年m月d日" }}</td>

 

ソート

全データを取得してソートしたいとき、ListViewの「ordering」属性を設定する。

todoapps/views.py


class TodoTopView(ListView):
    """TODOリスト一覧表示"""

    model = models.Todo
    template_name = "todo/top.html"
    context_object_name = "tasks"
    ordering = ["due_date"]

 

ordering = [“due_date”] ⇒ 期日の昇順
ordering = [“due_date”] ⇒ 期日の降順

 

WHERE条件の追加

一覧検索のWHERE条件を設定したい場合、静的な検索条件なら「queryset」属性を設定する。
動的に検索条件を作成する場合、「get_queryset」メソッドをオーバーライドする。

静的な検索条件

例えばステータスが0(未完了)のデータのみ抽出したいといったケースでは、以下のように実装する。

todoapps/views.py


class TodoTopView(ListView):
    """TODOリスト一覧表示"""

    model = models.Todo
    template_name = "todo/top.html"
    context_object_name = "tasks"
    queryset = models.Todo.objects.filter(status=0)

 

動的な検索条件

画面からのリクエスト内容などの条件をもとに、動的にWHERE条件を作成するケースでは、以下のように実装する。

todoapps/views.py


class TodoTopView(ListView):
    """TODOリスト一覧表示"""

    model = models.Todo
    template_name = "todo/top.html"
    context_object_name = "tasks"

    def get_queryset(self):
        return models.Todo.objects.filter(status=0).order_by("due_date")

 

ページネーション

ページネーション機能を追加したい場合、ListViewの「paginate_by」属性を設定するだけサーバー側の実装は終わる。
例えば以下は1ページあたり5件表示したいという設定。

サーバー側

todoapps/views.py


class TodoTopView(ListView):
    """TODOリスト一覧表示"""

    model = models.Todo
    template_name = "todo/top.html"
    context_object_name = "tasks"
    paginate_by = 5

    def get_queryset(self):
        return models.Todo.objects.filter(status=0).order_by("due_date")

 

画面側

タスク一覧画面にページネーション表示エリアを追加する。
実際のページネーションの記述はファイルを分けて管理している。

top.html


(略)
  <!-- ページネーションエリア -->
  {% include 'todo/components/pagination.html' %}
{% endblock %}

以下を実装すれば、ページネーション機能を画面に実装できる。

pagination.html


{% if page_obj %}
<div class="pagination mt-4">
  <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_obj.number }} / {{ 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>
{% endif %}

 

ページネーション表示例

 

 

 

 

スポンサーリンク