[Django] コメント機能の追加
この連続企画も今回も含めて残り二回となりました。
今回は、記事に対するコメントができる機能を実装します。
シリーズ一覧
コメント機能の追加
コメント機能を実現するにはデータベースのリレーションをうまく活用できれば実現できそうです。
モデル
新たにコメントのデータを保存するテーブル(モデル)を作ります。models.py
# 略
class Comment(models.Model):
text = models.TextField()
posted_at = models.DateTimeField(auto_now_add=True)
article = models.ForeignKey(to=Article, related_name='comments', on_delete=models.CASCADE)
def __str__(self):
return self.text
text = models.TextField()
がコメントの本文です。
Articleクラスのtext
と同様TextField
を利用しています。
posted_at = models.DateTimeField(auto_now_add=True)
がコメントの投稿日時です。
こちらも、Articleクラスのposted_at
と同様DateTimeField
を利用しています。
article = models.ForeignKey(to=Article, related_name='comments', on_delete=models.CASCADE)
がちょっと特殊で、コメントへのリレーションです。
ForeignKey
は一対多のリレーションを表します。
要は、データベースの外部キーです。
to="XXX"
(XXXはモデルクラス名)でどのテーブル(モデル)の要素に対してリレーションを持つか指定できます。
related_name='comments'
は、Articleクラスから逆参照するときにどのような名前で参照するかを指定しています。
今回の例だと、
article = models.Article.objects.get(pk=n) # 記事を取得
article.comments # <- これで、この記事に対するコメントへアクセスできる
article.comments.all() # とすることでこの記事に対するコメントを全件取得
on_delete=models.CASCADE
で参照先の記事が削除されたときのふるまいを指定しています。
models.CASCADE
を指定すると、参照先の記事が削除された際、その記事に対するコメントも削除します。
モデルを書き換えたので忘れずにマイグレーションをしましょう。
python manage.py makemigrations
出力
Migrations for 'blog':
blog\migrations\0003_comment.py
- Create model Comment
python manage.py migrate
出力
Operations to perform:
Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
Applying blog.0003_comment... OK
URL
今回は新たに設定することはありません。
テンプレート
コメントの投稿と、コメントが表示できるようにフォームなど、ちょっと書き足しました。
フォームの部分には{% csrf_token %}
を忘れずに指定しておきましょう。
view_article.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>{{ article.title }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<p><a href="{% url 'index' %}">トップページへ</a></p>
<p><a href="{% url 'article_all' %}">一覧へ戻る</a></p>
<h3>{{ article.title }}</h3>
<small>投稿日時 : {{ article.posted_at }}</small>
<small>最終更新 : {{ article.last_modify }}</small>
<p>{{ article.text }}</p>
<p><a href="{% url 'like' article.pk %}"><span id="like">{{ article.like }}</span>いいね!</a></p>
<form action="" method="POST">
<p><label for="com">コメント</label></p>
{% csrf_token %}
<textarea name="text" id="com" cols="30" rows="10"></textarea>
<button type="submit">投稿</button>
</form>
{% for comment in article.comments.all %}<!-- 記事に対するコメントを全件取得 -->
<p>{{ comment.text }}<br/><small>{{ comment.posted_at }}</small></p>
{% empty %}
<p>コメントはありません</p>
{% endfor %}
</body>
</html>
view
先ほどURLを追加しなかった代わりに、記事の閲覧ページを表示させているview_article
関数に改造を加えます。
具体的にはコメントが投稿されたとき、コメントをデータベースに保存する機能を追加します。
「投稿されたとき」なのでrequest.method == "POST"
で条件分岐を発生させます。
if request.method == "POST":
コメントの保存処理
データベースにデータを保存する方法を覚えていますか?
Model.objects.create(フィールド名=値)
で出来ます。
views.py
def view_article(request, pk):
template_name = "blog/view_article.html"
try:
article = models.Article.objects.get(pk=pk)
except models.Article.DoesNotExist:
raise Http404
if request.method == "POST":
# データベースに投稿されたコメントを保存
models.Comment.objects.create(text=request.POST["text"], article=article) # 追記
context = {"article": article}
return render(request, template_name, context)
これで、コメントが投稿できるはずなのでやってみてください。
まとめ
他のモデルへのリレーションはmodels.ForeignKey
で表現できる。
to
で参照先を指定on_delete
で参照先が削除されたときのふるまいを指定
最後に
コメント機能を実装して、だいぶ中身も充実したと思うので機能の追加は以上にしようと思います。
次回は、テンプレートをもう少し綺麗に書き直してページのデザインを整えたいと思います。
ページデザインにはBootstrap4を利用する予定です。