知識の枝

"All is well"

Djangoでコメント機能を作る

約435日前 2021年4月17日9:57
デジタル
Django HTML

改訂履歴


2021/4/17 投稿

1. 背景


Djangoを使った開発中に覚えたことを備忘録として残します。

今回はコメント機能の実装方法を記します。


2. ゴール


記事にコメントを付ける。


3. 備忘録


3.1 - やることのイメージ


このブログには投稿記事が複数あります。
それぞれの記事には専用のコメントフォームがあり、コメントを残すと記事の下にコメントとして表示されます。



実装の順序としては、

①Commentモデルをつくる

②コメントフォームをつくる

③コメントページのビューをつくる

④コメントページのhtmlをつくる

⑤その他設定

という流れで進めます。

3.2 - Commentモデル


models.pyにCommentモデルを作成しましょう。
models.py
class Comment(models.Model):
"""記事に紐づくコメント"""
name = models.CharField('名前', max_length=255, default='名無し')
text = models.TextField('本文')
target = models.ForeignKey(Post, on_delete=models.CASCADE, verbose_name='対象記事')
created_at = models.DateTimeField('作成日', default=timezone.now)

def __str__(self):
return self.text[:20]

まず前提として記事モデルが「Post」という名前で定義されているとします。

「Comment」という名前でモデルを作成しました。
class Comment(models.Model):


コメント投稿者の名前フィールドを作成します。
name = models.CharField('名前', max_length=255, default='名無し')


次にコメント本文のフィールドを作成します。
text = models.TextField('本文')


どの記事に対するコメントなのか分かるようにします。
target = models.ForeignKey(Post, on_delete=models.CASCADE, verbose_name='対象記事')
「Post」モデルから記事を1つ選択するフィールドです。


コメント投稿日時のフィールドも作成しておきます。
created_at = models.DateTimeField('作成日', default=timezone.now)


Commentモデルを管理する際にコメント1つ1つが本文の開始20文字で一覧表示されるようにします。
def __str__(self):
return self.text[:20]
(文字数制限をしないと管理画面がカオスになる為)

これでCommentモデルが完成しました。

下記コマンドでデータベースに登録します。
python manage.py makemigrations
python manage.py migrate


モデルの操作は以上です。


3.3 - コメントフォーム


models.pyやviews.pyがある階層に「forms.py」というファイルを作成しましょう。

forms.pyの中にコメント入力画面でどのフィールドを記入させるか決めておきます。
forms.py
from django import forms
from .models import Comment


class CommentCreateForm(forms.ModelForm):
"""コメントフォーム"""
class Meta:
model = Comment
exclude = ('target', 'created_at')

CommentCreateFormという名前でフォームを作成しました。
class CommentCreateForm(forms.ModelForm):


この投稿フォームの対象となるモデルを選択します。
model = Comment


対象記事投稿日時は自動で決まるようにする為、フォーム画面では表示されないようにします。
exclude = ('target', 'created_at')


これでコメント投稿画面を作成する準備が整いました。


3.4 - コメントページのビュー


views.pyにコメント投稿ページのビューを作ります。

views.py
class CommentCreate(generic.CreateView):
"""コメント投稿ページのビュー"""
template_name = 'comment_form.html'
model = Comment
form_class = CommentCreateForm

def form_valid(self, form):
post_pk = self.kwargs['pk']
post = get_object_or_404(Post, pk=post_pk)
comment = form.save(commit=False)
comment.target = post
comment.save()
return redirect('post:detail', pk=post_pk)

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['post'] = get_object_or_404(Post, pk=self.kwargs['pk'])
return context


ビュー名は「CommentCreate」としました。
クラスベースビューで「CreateView」を継承しています。
class CommentCreate(generic.CreateView):


ビューで表示するhtmlテンプレートを選択します。
ここではまだ未作成ですが「comment_form.html」という名前を仮入力しておきます。
htmlは3.5項で作成します。
template_name = 'comment_form.html'


Commentモデルをモデルとして選びます。
model = Comment


先ほど作ったコメントフォームを「form_class」に割り当てます。
form_class = CommentCreateForm


フォームに入力された情報が正しいときに実行されます。
    def form_valid(self, form):
post_pk = self.kwargs['pk']
post = get_object_or_404(Post, pk=post_pk)
comment = form.save(commit=False)
comment.target = post #Postモデルのidが「post_pk」のもの=2行目の部分
comment.save()
return redirect('post:detail', pk=post_pk)
フォーム入力画面でコメントの投稿対象記事「target」をあえて表示させていませんでしたね?
forms.py
exclude = ('target', 'created_at')

フォーム的には「name」と「text」のフィールドが入力された状態で投稿されていますので、form_validが動きます。
データの保存先となるCommentモデルには「target」という項目がありますので、この項目を入力してからsaveする必要があります。

まず一旦、投稿されたフォーム内容をCommentモデルインスタンスとして変数「comment」に保存します。
このとき(commit=False)としているので、実際のCommentモデルには保存されません。
comment = form.save(commit=False)


commentのtarget項目が空っぽのままなので入力します。
comment.target = post


そしてようやくCommentモデルを保存します。
comment.save()


保存後は記事の詳細ページに戻るようになっています。
return redirect('post:detail', pk=post_pk)
"post:detail"はurls.pyのapp_nameとpathのnameの関係です。
urls.py
from django.urls import path
from . import views

app_name = 'post' #ここ

urlpatterns = [
path('<int:pk>', views.PostDetail, name='detail'), #ここ
]


最後に「get_context_data」の部分です。
ここではhtmlテンプレートに渡すデータを定義しています。
    def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['post'] = get_object_or_404(Post, pk=self.kwargs['pk'])
return context



3.5 - htmlテンプレート


3.4項でちらっと出てきたhtmlテンプレートを作成します。
template_name = 'comment_form.html'

「comment_form.html」というファイルを作ります。
comment_form.html
<div class="container col-lg-6 offset-lg-3">
<p class="titleline">{{ post.title }}</p>
<form action="" method="POST" id="comment-form">
{{ form.non_field_errors }}
{% for field in form %}
<div class="field">
{{ field.label_tag }}
{% render_field field class="form-control" %}

{{ field.errors }}
</div>
{% endfor %}
{% csrf_token %}
<div class="row my-3">
<button type="submit" class="btn btn-success col-3 offset-2">投稿する</button>
<a class="btn btn-danger col-3 offset-2" href="{% url 'post:detail' post.pk %}">キャンセル</a>
</div>
</form>
</div>
Bootstrap CSSを適用させている為、「class="○○○"」が多いですが、この部分は無くても動きます。

投稿フォームの出来上がりイメージ



フォームを入力して「投稿する」ボタンを押すと、該当記事のコメント欄にコメントが表示されます。

記事詳細ページのhtml例です。
{% for comment in comments %}
<div class='border-bottom'>{{ comment.name }} {{ comment.created_at }}</div><div class='mt-2'>{{ comment.text }}</div>
{% endfor %}

views.pyの記事詳細ビューでCommentモデルを"comments"という名前で渡すことをお忘れなく。
views.py
def PostDetail(request, pk):
"""Article page"""
detail = get_object_or_404(Post, pk=pk)

context = {
"detail": detail,
"comments": Comment.objects.filter(target=detail.id) #該当記事のコメントだけを渡します。
}



3.6 - その他設定


urls.pyにコメント投稿ページへのパスを登録しておきましょう。
urls.py
from django.urls import path
from . import views

app_name = 'post'

urlpatterns = [
path('', views.PostView, name='post'),
path('<int:pk>', views.PostDetail, name='detail'),
path('comment/create/<int:pk>/', views.CommentCreate.as_view(), name='comment_create'), #ここ
]



5. さいごに


無事コメントページができましたか?
引き続き勉強して理解を深めたいと思います。

関連記事も参考にして下さい。
名無し 約239日前 2021年10月30日17:10 返信する
たい
名無し 約178日前 2021年12月30日13:58 返信する
添付ファイルダウンロード(views.py)
初めまして。記事を参考に自分でもコメント機能を作成しようとしたのですが、ビューがクラスベースなのでコメントの拾い方が分からず、フォームも表示されない状態になってしまいます。どのようにすればよいでしょうか。
chuna 約176日前 2022年1月1日12:30
発生している問題の事象は
「コメント投稿ページに投稿用のフォームが表示されない」ということでしょうか?

ファイルの中身を確認しましたが、views.pyを見た限りは特段問題が無いように感じました。
もし差し支え無ければ、forms.py、templatesフォルダの.html、models.py、urls.py等 一式をお送り頂けますと、原因を突き止めやすくなります。
chuna.technology@gmail.com 宛に直接送って頂いても問題ありませんので、宜しくお願い申し上げます。