概要
クラスベースビュー(class-based view)でタスク更新機能を作成したのでまとめた。
タスク更新機能は、Djangoがもともと用意しているUpdateViewクラスを継承して作成する。
UpdateViewをどのように使用するのかを紹介する。
前提
以下の記事の続きとなる。
概要 クラスベースビュー(class-based view)でタスク登録機能を作成したのでまとめた。 タスク登録機能は、Djangoがもともと用意しているCreateViewクラスを継承して作成する。 CreateViewをどのよ[…]
画面遷移
今回実装するタスク編集機能の画面イメージは以下となる。
タスク編集画面遷移
入力エラーの場合
タスク編集機能の実装
サーバー側
ルーティング
todoapps/urls.py
from django.urls import path
from . import views
urlpatterns = [
path("tasks/", views.TodoTopView.as_view(), name="top"),
path("detail/<int:pk>/", views.TodoDetailView.as_view(), name="detail"),
path("task/create/", views.TodoCreateView.as_view(), name="create_task"),
path("task/edit/<int:pk>", views.TodoEditView.as_view(), name="edit_task"),
]
上記により、サーバーを起動して「http://localhost:8000/task/edit/XXX」にアクセスすると、viewsファイルに定義しているタスク編集ビュー(TodoEditViewクラス)の処理を呼ぶ。
タスク編集ビュー
todoapps/views.py
from django.views.generic import ListView, DetailView, CreateView, UpdateView
(略)
class TodoEditView(UpdateView):
"""タスク編集画面"""
# DB更新モデル
model = models.Todo
# テンプレート
template_name = "todo/edit.html"
# タスク編集フォーム
form_class = forms.TodoEditForm
def get_context_data(self, **kwargs):
"""コンテキストをオーバーライドする"""
context = super().get_context_data(**kwargs)
# カテゴリ一覧の設定
context["categories"] = models.TodoCategory.objects.all()
return context
def get_success_url(self):
return reverse_lazy("detail", kwargs={"pk": self.object.pk})
class TodoEditView(LoginRequiredMixin, UpdateView)
編集画面の表示処理を作成するには、クラスベースビューのUpdateViewを継承して作成する。
これにより、親クラスの更新処理をカスタマイズできる。
# タスク編集フォーム
form_class = forms.TodoEditForm
画面入力フィールドに関するフォームクラスは、forms.pyに定義した編集画面用フォームクラスを参照する。
def get_context_data(self, **kwargs):
"""コンテキストをオーバーライドする"""
context = super().get_context_data(**kwargs)
# カテゴリ一覧の設定
context["categories"] = models.TodoCategory.objects.all()
return context
タスク登録画面同様に、カテゴリのセレクトボックスを編集画面に表示する必要がある。
そのため、画面に渡すコンテキスト(辞書データ)にDBから取得したカテゴリ一覧を追加する。
def get_success_url(self):
"""更新完了後のリダイレクトURLを返却する"""
return reverse_lazy("detail", kwargs={"pk": self.object.pk})
更新完了後にはタスク詳細画面に遷移するが、詳細画面はパラメータとしてタスクIDを送る必要がある。
self.objectの中身はDB更新した後のタスクオブジェクトを保持している。
reverse_lazyのkwargsを指定するパターンは、URLパターンに含まれるキーワードを指定することができる。
上記のように辞書データでプライマリキーを指定しているのは、urls.pyにてpkを指定しているためとなる。
※todo/urls.pyの「detail」を参照
補足(success_url属性とget_success_urlメソッドの違いについて)
success_url属性にURLを設定すると、固定URLへリダイレクトする。
一方で、動的なURLへリダイレクトさせたいとき(例えばクエリパラメータを設定したいとき)は、get_success_urlメソッドにURLを設定する必要がある。
タスク編集フォーム
todoapps/forms.py
from django import forms
from . import models
class TodoForm(forms.ModelForm):
"""タスク登録フォーム"""
class Meta:
# 入力チェック生成対象のモデル
model = models.Todo
# 画面入力フィールド
fields = ["task", "memo", "due_date", "category"]
class TodoEditForm(TodoForm):
"""タスク編集フォーム"""
class Meta(TodoForm.Meta):
# 編集画面ではステータスの状態を画面に表示するため、statusを追加
fields = ["task", "memo", "due_date", "category", "status"]
class TodoEditForm(TodoForm):
"""タスク編集フォーム"""
class Meta(TodoForm.Meta):
タスク登録フォームであるTodoFormを継承して、タスク編集フォームとしている。
フォームを継承する場合、上記のように各クラスで継承元を記述する必要があるらしい。
class Meta(TodoForm.Meta):
# 編集画面ではステータスの状態を画面に表示するため、statusを追加
fields = ["task", "memo", "due_date", "category", "status"]
タスク登録画面では、登録するとステータスは0(未完了)固定とするため、画面に表示していない。
しかし、タスク編集画面ではステータスを更新できるようにしているため、画面表示する必要がある。
そのため、タスク登録画面用のフォームを継承してステータスだけ追加している。
画面側
編集ボタン
templates/todo/detail.html
<a href="{% url 'edit_task' todo.id %}" class="btn btn-primary mb-3 me-3">編集する</a>
urls.pyに定義したタスク編集ビューにリクエストしている。
※パラメータとしてtodo.idとしているが、todo.pkとしても問題ない
タスク編集画面
templates/todo/edit.html
{% extends "base.html" %}
{% load todo_template_filter %}
{% block title %}タスク編集{% endblock %}
{% block main %}
<div class="container">
<div class="row justify-content-center mt-3">
<div class="col-md-8">
<a href="{% url 'detail' form.instance.pk %}" class="btn btn-primary mb-3">戻る</a>
<div class="card">
<div class="card-header text-center">
<h4>タスク編集</h4>
</div>
<div class="card-body">
<form method="post">
{% csrf_token %}
<div class="form-group">
<label for="task">タスク<span class="text-primary ml-3">※必須</span></label>
<input type="text" class="form-control" id="task" name="task" value="{{ form.task.value }}">
</div>
{% for error in form.task.errors %}
<div id="title-error" class="text-danger mb-3 error-msg">
{{ error }}
</div>
{% endfor %}
<div class="form-group">
<label for="memo">メモ</label>
<textarea class="form-control" id="memo" name="memo" rows="3">{{ form.memo.value|default_if_none:''}}</textarea>
</div>
{% for error in form.memo.errors %}
<div id="title-error" class="text-danger mb-3 error-msg">
{{ error }}
</div>
{% endfor %}
<div class="form-group">
<label for="due_date">期日<span class="text-primary ml-3">※必須</span></label>
<input type="date" class="form-control" id="due_date" name="due_date" value="{{ form.due_date.value|to_date }}">
</div>
{% for error in form.due_date.errors %}
<div id="title-error" class="text-danger mb-3 error-msg">
{{ error }}
</div>
{% endfor %}
<div class="form-group">
<label for="category">カテゴリ<span class="text-primary ml-3">※必須</span></label>
<select class="form-control" id="category" name="category">
<option value="">選択してください</option>
{% for category in categories %}
<option value="{{ category.id }}" {% if category.id|to_string == form.category.value|to_string %}selected{% endif %}>{{ category.category_name }}</option>
{% endfor %}
</select>
</div>
{% for error in form.category.errors %}
<div id="title-error" class="text-danger mb-3 error-msg">
{{ error }}
</div>
{% endfor %}
<div class="form-group">
<label for="status">ステータス<span class="text-primary ml-3">※必須</span></label>
<select class="form-control" id="status" name="status">
<option value="">選択してください</option>
<option value="0" {% if form.status.value|to_string == '0' %}selected{% endif %}>未完了</option>
<option value="1" {% if form.status.value|to_string == '1' %}selected{% endif %}>完了</option>
</select>
</div>
{% for error in form.status.errors %}
<div id="title-error" class="text-danger mb-3 error-msg">
{{ error }}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary mt-3">更新</button>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
基本的にタスク登録画面と構成は同じ。
<input type="text" class="form-control" id="task" name="task" value="{{ form.task.value }}">
タスク編集画面を表示する際に、初期値を設定するため上記のようにvalueを設定している。
※タスク登録画面でも上記のように記述すると、入力エラー時に画面入力情報を保持してくれる
<a href="{% url 'detail' form.instance.pk %}" class="btn btn-primary mb-3">戻る</a>
【戻る】ボタンを押下すると前の画面(詳細画面)に遷移させる。
詳細画面ビューのリクエストパラメータにはform.instanceとしているが、これは編集画面表示時のDBから取得したタスク情報になる。