前回
####1. Djangoを使ったCRUD機能実装&Herokuデプロイ
アソシエーション実装(1対多)
以前に作成したブログに対して、コメントを投稿する機能を追加していきます。
モデルの作成
新たにComment
というモデルを追加します。
この時、ブログとアソシエーションを組むためにForeignKey
を使って以下のように記述します。
[blog_app/models.py]
from django.db import models
class Blog(models.Model):
title = models.CharField(max_length=100)
content = models.TextField(max_length=300)
# 以下を追加
class Comment(models.Model):
content = models.TextField(max_length=300)
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
ForeignKey
を設定したモデルには新たに親モデル名_id
というカラムが追加されます。
この場合は、コメントモデルにblog_id
カラムが追加されます。
on_delete=models.CASCADE
は、親が削除された際に子も削除するためのオプションです。
on_delete
に設定できるオプションは他にも色々と用意されています。
他のオプションについてはこの記事で非常にわかりやすくまとめられています。
models.py
を編集した内容を反映させるためのマイグレーションファイルを作成する。
$ python manage.py makemigrations
マイグレーションファイルの内容をデータベースに反映する
$ python manage.py migrate
フォームの追加
[blog_app/views.py]
from .models import Comment
# 省略
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['content']
テンプレートの編集
今回は、ブログの詳細画面にコメントを投稿できる機能を追加します。
[blog_app/templates/blog_app/detail.html]
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>blog_app</title>
</head>
<body>
<h1>Detail</h1>
<table>
<tr>
<th>ID</th>
<td>{{obj.id}}</td>
</tr>
<tr>
<th>TITLE</th>
<td>{{obj.title}}</td>
</tr>
<tr>
<th>CONTENT</th>
<td>{{obj.content}}</td>
</tr>
</table>
<!-- 以下を追加 -->
<form action="{% url 'detail' id %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="click">
</form>
{% for comment in comments %}
<p>{{comment.content}}</p>
{% endfor %}
<!-- ここまで -->
</body>
</html>
ビューの修正
[blog_app/views.py]
# 省略
from .models import Comment
from .forms import CommentForm
def detail(request, num):
blog = Blog.objects.get(id=num)
comments = blog.comment_set.all()
if (request.method == 'POST'):
obj = Comment(blog_id=num) # ポイント1
comment = CommentForm(request.POST, instance=obj)
comment.save()
return redirect(to=f'/blog_app/detail/{num}')
params = {
'id': num,
'obj': blog,
'form': CommentForm(),
'comments': comments,
}
return render(request, 'blog_app/detail.html', params)
ポイント1:コメントのインスタンス作成時に、生成するコメントのblog_id
にnum
を入れておくことで、ブログとのアソシエーションしたコメントが生成される。
サーバを起動してコメントが投稿できるようになていればOK。
確認ダイアログの実装
削除ボタンを押した際に、削除用のページに遷移することに違和感があるので、確認ダイアログを表示させる仕様に変更します。
[blog_app/templates/blog_app/index.html]
<!-- 省略 -->
<td><a href="{% url 'delete' item.id %}" onclick='return confirm("本当に削除しますか?");'>Delete</a></td>
<!-- 省略 -->
delete
関数を以下のように編集。
[blog_app/views.py]
def delete(request, num):
blog = Blog.objects.get(id=num)
blog.delete()
return redirect(to='/blog_app')
あとは、不要になったdelete.html
を削除しましょう。