概要
クラスベースビュー(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を継承してクラスを作成することで、一覧表示ビュー機能を作成できる。
実装イメージ
ルーティング
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クラスが見えないところで実施してくれているイメージ。
・画面に表示するためのキーワード変数にデータを代入
・レスポンス返却
画面側
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”] ⇒ 期日の降順
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">«</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 }}">»</a>
{% endif %}
</span>
</div>
{% endif %}
ページネーション表示例