DjangoでToDoList開発Part6
こんにちは、にわこまです。
今回は、ToDoの内容を変更できるページを作成します。締切日の延長や完了/未完了の編集を行うために作成します。
「DjangoでToDoList開発」シリーズはDjangoのチュートリアルとして利用されることを目的としています。そのため、誤字脱字や分かりにくい点がございましたら、ご連絡お願い致します。
スポンサードサーチ
DjangoでToDoList開発Part6
今回は、ToDoを編集することができる編集ページを作成します。
完成イメージとしては、トップページに表示されているToDoのタイトルをクリックすることで、そのToDoの編集ページに移動することができるようにします。編集後は自動でトップページに移動するようにリダイレクトを設定します。
開発手順は、Part5で作成した削除ページをほぼ同じです。編集用フォーム(forms.py)の作成、編集ページ(todoEdit.html)の作成、表示処理と編集処理(views.py)の作成の順で開発を行います。
forms.pyの編集
編集ページで表示するデータを、ひとまとめにするフォームを作成します。todolist/forms.pyを編集します。ファイルの1番下の行に追記します。
class todoEditForm(forms.Form):
title = forms.CharField(label="Title", max_length=200)
contents = forms.CharField(label="Contents", widget=forms.Textarea)
deadline = forms.DateField(label="Deadline")
done = forms.BooleanField(label="Completed?", required=False)
記入日は自動でデータが格納されるため、記入日以外をフォームに設定します。
コードの解説
2行目の「title」は、ToDoのタイトルを表示するフォームです。モデルやtodoAddForm、todoDeleteFormと同じくCharFieldを設定します。さらに、max_lengthを200と設定します。
3行目の「contents」は、ToDoの内容を表示するフォームです。todoAddFormやtodoDeleteFormと同じくCharFieldを設定します。また、widgetはTextareaとします。
4行目の「deadline」は、締切日を表示するフォームです。モデルやtodoDeleteFormと同じくDateFieldを設定します。
5行目の「done」は、ToDoの完了/未完了を表示するフォームです。モデルと同じくBooleanFieldを設定します。また、requiredをFalseに設定します。
requiredは、値の代入が必須であるかないかについて設定するためのものです。デフォルトはrequired=Trueと設定されています。つまり、値の代入が必須であるという設定になっています。
フォームでは、「csrf_token」が設定されるため空のデータを送ることができません。しかし、required=Falseと設定することで空のデータを送ることができます。
スポンサードサーチ
編集ページの作成
templates/todolist/にtodoEdit.htmlを作成し、編集します。
{% extends 'todolist/base.html' %}
{% block title %}ToDo Edit{% endblock %}
{% block contents %}
<article class="todoedit">
<div class="page-name">
<h1>ToDo編集フォーム</h1>
</div>
<div class="todoedit-form">
<form action="{% url 'todoEdit' edit_id %}" method="post">
{% csrf_token %}
{% for field in form %}
<div class="field-row">
{{ field.label_tag }}{{ field }}
</div>
{% endfor %}
<input type="submit" value="Save">
</form>
</div>
</article>
{% endblock %}
コードの解説
1行目の「{% extends ‘todolist/base.html’ %}」は、base.htmlを拡張してtodoEdit.htmlを作成するという宣言です。
3行目の「{% block title %}ToDo Edit{% endblock %}」は、タイトルブロックにToDo Editを代入しています。
5行目~22行目は、contentsブロックに代入するコードです。 12行目の「{% csrf_token %}」は、アプリケーションの脆弱性を突く攻撃を防ぐためのものです。formを書くならば、必ず必要なものです。
csrfとは、クロスサイトリクエストフォージェリのことです。アプリケーションの脆弱性を突く攻撃方法のことです。
13行目~17行目は、forブロックです。繰り返し処理がされています。formからlabelとfieldが取り出されています。
最後に、11行目で指定したURL「todoEdit」は、後でurls.pyで指定するため覚えておきます。
現在のフォルダ状況(templates/todolistから)
templates
|- todolist
|- base.html
|- index.html
|- todoAdd.html
|- todoDelete.html
|- todoEdit.html # 追加部
views.pyの編集(表示処理)
フォームでひとまとめにしたデータを表示する処理を作成します。todolist/views.pyを編集します。
from django.shortcuts import render
from django.shortcuts import redirect
from django.http import HttpResponse
from .models import MyToDoList
from .forms import todoAddForm, todoDeleteForm, todoEditForm # todoEditFormを追加
・
・
・
省略
・
・
・
# === ここから 追加 ===
def todoEdit(request, edit_id):
mtdl_obj = MyToDoList.objects.filter(id=edit_id).first()
context = {
'title':mtdl_obj.title,
'contents':mtdl_obj.contents,
'deadline':mtdl_obj.deadline,
'done':mtdl_obj.done,
}
form = todoEditForm(context)
params = {
‘edit_id’:edit_id,
'form':form,
}
return render(request, 'todolist/todoEdit.html', params)
# === ここまで 追加 ===
コードの解説
7行目は、todoEditFormを追加しました。「forms.pyにあるtodoEditFormを使う。」という宣言を行っています。
17行目の「mtdl_obj = MyToDoList.objects.filter(id=edit_id).first()」は、編集するToDoのデータをMyToDoListモデルから抽出しています。
18行目~23行目は、todoEditFormに削除するToDoのデータを辞書型で設定しています。辞書型のkeyの文字列は、todoEditFormで設定した変数名です。
24行目の「form = todoEditForm(context)」は、todoEditFormに編集するToDoのデータを対応させています。これにより、編集ページでは、空欄のフォームではなく値が入ったフォームが表示されます。
25行目~28行目は、todoEdit.htmlで使う変数を辞書型で設定しています。edit_idはURLのところで使います。
29行目は、renderによってtodoEdit.htmlにparamsを指定しています。これにより、todoEdit.html内でedit_idとformが使えるようになります。
urls.pyの編集
viewを作成したためURLを設定します。todolist/urls.pyを編集します。
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('todoAdd', views.todoAdd, name='todoAdd'),
path('todoDelete/<int:del_id>', views.todoDelete, name='todoDelete'),
path('todoEdit/<int:edit_id>', views.todoEdit, name='todoEdit'), # 追加部
]
pathの第1引数「’todoEdit/<int:edit_id>’」は、スラッシュ移行のedit_idによって編集したいToDoを指定することができます。また、edit_idの部分はviews.pyの関数の引数に指定したものを代入します。
「path(‘aaa/<int:x>’, views.bbb, name=’ccc’)」のようなURLをHTMLファイル内で使いたい場合は、「{% url ‘ccc’ x %}」のように記述します。xは数字です。
今回追加したURLの名前は、todoEdit.htmlでformに設定したURLです。具体的には、pathの第3引数「name=’’」にtodoEditを指定します。
動作確認
編集したいデータが正しく表示されるか確認するため、サーバを起動します。
python manage.py runserver
サーバを起動させたら、「http://localhost:8000/todolist/todoEdit/2」にアクセスします。最後の数字が「2」でエラーが発生する場合は他の数字で試します。私の場合は以下のように表示されました。
データが表示されたことを確認したら、適当にデータを編集してSaveボタンをクリックします。(私は「Completed?」をFalseからTrueに変更しました。)
その後、トップページに移動します。私の場合は以下のように表示されました。
編集したはずのデータが編集されていないことを確認できると思います。views.pyのtodoEditに編集処理を作成していないからです。
スポンサードサーチ
views.pyの編集(編集処理)
Saveボタンを押した後の編集処理を作成します。todolist/views.pyを編集します。
def todoEdit(request, edit_id):
mtdl_obj = MyToDoList.objects.filter(id=edit_id).first()
# === ここから 追加 ===
if(request.method == 'POST'):
if('done' in request.POST):
mtdl_obj.done = True
else:
mtdl_obj.done = False
mtdl_obj.title = request.POST['title']
mtdl_obj.contents = request.POST['contents']
mtdl_obj.deadline = request.POST['deadline']
mtdl_obj.save()
return redirect(to='/todolist/')
# === ここまで 追加 ===
context = {
'title':mtdl_obj.title,
'contents':mtdl_obj.contents,
'deadline':mtdl_obj.deadline,
'done':mtdl_obj.done,
}
form = todoEditForm(context)
params = {
'edit_id':edit_id,
'form':form,
}
return render(request, 'todolist/todoEdit.html', params)
コードの解説
4行目~13行目は、Saveボタンがクリックされた後の処理です。
5行目~8行目は、完了/未完了のデータを受け取っています。「Completed?」のチェックボックスにチェックがされていない状態でSaveボタンをクリックすると、doneのデータは送られないためif文によってデータが送られて来たかどうかを判定しています。
チェックされた状態でSaveボタンがクリックされた場合、request.POST[‘done’]には「on」というデータが入っています。しかし、doneのデータ型はBooleanであるため、データが送られた場合は「True」、データが送られてこなかった場合は「False」のようにしています。
9,10,11行目は、送られてきたデータを編集したいデータに代入しています。
12行目は、新たに代入されたデータを保存しています。
13行目は、redirect関数によってトップページにリダイレクトします。
トップページの編集
「views.pyの編集(表示処理)」の動作確認で行ったような、edit_idを手動で指定する方法はエラーが発生する可能性があります。そのためトップページの「タイトル」に、そのデータに対応する編集ページへ移動するリンクを追加します。
templates/todolist/index.htmlを編集します。
{% for tdl in todolist %}
<tr class="tdl">
<td class="title"><a href="{% url 'todoEdit' tdl.id %}">{{ tdl.title }}</a></td> # 変更部
<td class="entry-date">{{ tdl.entryDate|date:"n/j" }}</td>
<td class="deadline">{{ tdl.deadline|date:"n/j" }}</td>
<td ckass="done">
{% if tdl.done == False %}
<label>未</label>
{% elif tdl.done == True %}
<label>済</label>
{% endif %}
</td>
<td class="btn-del"><a href="{% url 'todoDelete' tdl.id %}">Delete</a></td>
</tr>
{% endfor %}
動作確認
トップページのタイトルから編集ページに移動できるかの確認と編集したデータの確認を行うため、サーバを起動します。
python manage.py runserver
サーバを起動させたら、「http://localhost:8000/todolist/」にアクセスします。私の場合は以下のように表示されました。
いずれかのタイトルをクリックして、編集ページに移動します。私の場合は以下のように表示されました。
その後、適当なデータを編集してSaveボタンをクリックします。自動でトップページへ移動したあと、データが編集されているか確認を行います。(私はCompleted?を変更しました。)
トップページにて、データが編集されていることを確認することができたら動作確認と編集ページの作成は完了です。
まとめ
今回は、編集ページを作成しました。編集用のフォームを作成してデータを代入することでブラウザでは、データが入力された状態で表示させることができます。
編集ページは、データ1つひとつのページが必要であるためURLによってページを分けられるようにしました。
「path(‘aaa<int:x>’, views.bbb, name=’ccc’)」のようなURLをHTMLファイルで使用するためには、「{% url ‘ccc’ x %}」のように記述します。この場合xは数字です。
int以外にも指定できるものがあります。
参考サイト:公式Django docs:URL dispatcher
編集処理において、「done」の処理が特殊であったため気を付けて下さい。
次回は、完成に向けて仕上げを行います。具体的には、メニューの追加、全ページの装飾、ページネーションの追加を行います。
最後までお読みいただきありがとうございます。
スポンサードサーチ