Djangoアプリの投稿詳細ページにコメント機能を実装する方法について解説します。コメント機能はブログや掲示板、SNSなど多くのアプリケーションで使われる標準的な機能です。
※この記事では弊サイトの Django入門編 を終えている前提で進めます。また、機能の実装先もDjango入門編で制作した掲示板アプリをベースに進めていきます。
目次
完成イメージ

このように投稿詳細ページにコメント一覧の表示と、コメント投稿フォームを用意します。
テーブル設計

掲示板アプリの投稿用のArticlesテーブルに紐づいたコメント用のCommentsテーブルを作成します。
投稿者情報はDjangoが予め用意してくれているUserモデルと紐づけます。
あとはコメント内容用のcontentフィールドとコメントの作成日時用のcreated_atフィールドを用意します。
モデル
from django.db import models
from django.urls import reverse
class Article(models.Model):
author = models.ForeignKey(
'auth.User',
on_delete=models.CASCADE,
)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.content
def get_absolute_url(self):
return reverse('bbs:detail', kwargs={'pk': self.pk})
class Comment(models.Model):
author = models.ForeignKey(
'auth.User',
on_delete=models.CASCADE,
)
target = models.ForeignKey(
Article,
on_delete=models.CASCADE,
)
content = models.TextField('コメント')
created_at = models.DateTimeField(auto_now_add=True)
上記のテーブル設計をモデルに落とし込みます。
モデルの作成が完了したらマイグレーションでデータベースに反映します。
python manage.py makemigrations bbs python manage.py migrate
これでテーブルの用意ができました。
ルーティング
bbs/urls.pyにコメント作成機能のURLを追記します。
from django.urls import path
from . import views
app_name = 'bbs'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('create/', views.CreateView.as_view(), name='create'),
path('comment/<int:pk>/', views.CommentView.as_view(), name='comment'),
path('<int:pk>/update/', views.UpdateView.as_view(), name='update'),
path('<int:pk>/delete/', views.DeleteView.as_view(), name='delete'),
]
フォーム
次にforms.pyを作成してコメント作成用のフォームを定義します。
from django import forms
from .models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('content',)
ビュー
from .models import Article, Comment # 追加
from .forms import CommentForm # 追加
from django.shortcuts import redirect, get_object_or_404 # 追加
# 中略
class DetailView(generic.DetailView):
model = Article
# 追加
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# テンプレートにコメント作成フォームを渡す
context['comment_form'] = CommentForm
return context
# 中略
class CommentView(LoginRequiredMixin, generic.edit.CreateView):
model = Comment
form_class = CommentForm
#格納する値をチェック
def form_valid(self, form):
form.instance.author = self.request.user
article_pk = self.kwargs.get('pk')
article = get_object_or_404(Article, pk=article_pk)
comment = form.save(commit=False)
comment.target = article
comment.save()
return redirect('bbs:detail', pk=article_pk)
models.pyのCommentクラスをインポート。加えてリダイレクトと404ページ表示用のショートカットもインポートします。
コメント欄は投稿詳細ページに配置するのでDetailViewクラスに、コメント作成フォームをテンプレートに渡すためのコードを追記します。
汎用ビューのCreateViewを継承してCommentViewクラスを作成し、フォーム送信時に値をデータベースに格納しその投稿詳細ページにリダイレクトするよう記述します。
テンプレート
article_detail.htmlを修正し、コメント一覧とコメント欄を表示します。
{% extends "base.html" %}
{% block content %}
<h1>{{ article.id }}の投稿詳細ページ</h1>
<div class="container">
<p>{{ article.author }}:{{ article.created_at }}</p>
<p>{{ article.content }}</p>
<!-- ユーザー情報がその投稿のものと一致する場合 -->
{% if request.user.id == object.author_id %}
<p><a href='{% url "bbs:update" article.pk %}'>編集</a></p>
<p><a href='{% url "bbs:delete" article.pk %}'>削除</a></p>
{% endif %}
</div>
<!-- コメントを表示 -->
<div class="comment">
<h2>コメント一覧</h2>
{% for comment in article.comment_set.all %}
<div class="comment-content">
<p>{{ comment.author }}:{{ comment.created_at }}</p>
<p>{{ comment.content }}</p>
</div>
{% endfor %}
</div>
<!-- コメント作成フォームを表示 -->
<div class="comment-form">
<h2>コメント投稿</h2>
<form action='{% url "bbs:comment" article.pk %}' method="post">
{% csrf_token %}
{{ comment_form}}
<div>
<button type="submit">送信</button>
</div>
</form>
</div>
<p><a href='{% url "bbs:index" %}'>一覧ページへ戻る</a></p>
{% endblock %}
cssを少しいじりたいのでbase.htmlにも少し追記しておきましょう。
※本格的にデザインするならCSSはstyle.cssファイルに分割すべきです。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Djangoを使ってみよう!</title>
<style>
.container {
background-color: #efefef;
}
label {
display: block;
}
</style>
</head>
<body>
{% block content %}
{% endblock %}
</body>
</html>
動作確認

投稿詳細ページにコメント一覧とコメント投稿フォームが表示できています。
コメントを入力して送信すると
コメント投稿用のビューが呼び出され、データベースに保存されます。
保存されたコメントもきちんと表示されていますね。
コメントが1件もない場合はコメント一覧を表示しないようにした方が良さそうですが、弊サイトの Django入門編 でカバーしている範囲ですので、この記事はここまでとします。








content = models.TextField(‘コメント’)の部分なのですが、文字を入力する四角の部分は大丈夫なのですが、表示画面を拡大していくと、 コメント: の部分が四角の上の部分ではなく、四角の左のほうにずれてしまいす。
コメント:の部分だけ独立させたいといろいろ調べたのですが、うまくできずに詰まっています。
どのようにすればいいか聞きたいと思いコメントいたしました。
返信いただければ幸いです。
投稿詳細ページ(DetailView)の画面でお間違いないでしょうか。
まさにその画面の「コメント:」が四角の左のほうに来ないように、本記事の「テンプレート」の項でbase.html(の中でCSS)を修正し
label {
display: block;
}
という記述を入れてその部分をブロック要素にしています。
この作業が抜けてしまっていないかご確認下さい。
返信ありがとうございます。
ご指摘いただいた部分の作業を行ったあと実行を行った結果
「コメント:」がまた左側に行くという事態に陥ったのですが、
F12キーを押して該当部分を見てみると
label { _reboot.scss:383
display: inline-block;
}
_reboot.scss:383で優先となっていたためその処理を上書きするために
label {
display:block !important;
}
!important;を書き込むことで解決しました。
返信いただきありがとうございます
コメント投稿ボタンを押すと、画面にはHTTPエラー405と表示され、ターミナルには以下のエラーが出ます。
>Method Not Allowed (POST): /post/1/
>Method Not Allowed: /post/1/
コメント機能に関してはこちらの内容をまねているだけなのですが、何か思い当たる原因などありますでしょうか。
405エラー(405 Method Not Allowed)は、許可されていないHTTPのメソッドでアクセスを試みた場合に発せられるエラーです。
つまりリクエストは届いているけれどサーバー側でアクセスを拒否しているわけなので、環境側の問題かもしれません。
あとはルーティング設定に間違いがないかご確認ください。
この記事では投稿詳細ページのURLが int:pk (つまり/bbs/1/)となっており、’bbs:detail’で呼び出せるようにしていますが、アプリ名やルート設定が異なる場合、それに応じてリダイレクト先などのURLも変更する必要があります。