Django

Django 記事詳細ページにコメント機能をつける方法

公開日:2021-09-23 更新日:2023-06-06

Djangoで記事詳細ページ内にコメントフォームをつける方法について紹介します。

モデル

ポストモデルとコメントモデルを用意します。

from django.db import models


class Post(models.Model):
    title = models.CharField('タイトル', max_length=32)
    text = models.TextField('本文')


class Comment(models.Model):
    """記事に紐づくコメント"""

    text = models.TextField('本文')
    target = models.ForeignKey(Post, on_delete=models.CASCADE, verbose_name='対象記事')

今回は必要最小限の実装です。

コメントモデルのtargetにどの記事に対するコメントか、という情報を保存します。

URLパターン

アプリ内のurls.pyです。

from django.urls import path
from . import views

app_name = 'blog'

urlpatterns = [
    path('', views.PostList.as_view(), name='post'),
    path('detail/<int:pk>/', views.PostDetail.as_view(),
         name='post_detail'), 
]

とりあえず記事一覧ページと、記事詳細ページのURLを定義します。

ビュー

まずは記事一覧ページと詳細ページを作ります。

from django.views import generic
from .models import Post, Comment

class PostList(generic.ListView):
    model = Post
    template_name = 'blog/index.html'


class PostDetail(generic.DetailView):
    model = Post
    template_name = 'blog/post_detail.html'

記事詳細テンプレート

一覧ページは省略します。

記事詳細ページのHTMLは下記のような形です。

{% extends "blog/base.html" %}
{% block content %}
<h2>記事詳細ページです</h2>
<div class="title">
  {{ post.title }}
</div>
<div class="text">
  {{ post.text }}
</div>
<div class="comment">
  <h2>コメント</h2>
  {% for comment in post.comment_set.all %}
  <div class="comment-text">
    <p>{{ comment.text }}</p>
  </div>
  {% endfor %}
</div>

{% endblock %}
{% block extrahead %}
<style media="screen">
  .title {
    font-size: 2rem;
  }

  .text {
    margin-top: 30px;
  }

  .comment {
    margin-top: 30px;
  }

  .comment-text {
    margin-top: 20px;
  }

</style>
{% endblock %}

管理サイトから記事を一つとコメントを一つ追加するとこのような見た目になります。

雑ですがこんな感じで記事とコメントが表示されます。

ここのページ内でコメント機能をつけていきたいと思います。

詳細ページにコメント機能をつける

通常、このような構成の場合、コメント用の作成ビューと表示テンプレートを用意して行います。

ただし、コメントをする際に、ページ遷移をするのは利便性がよくないです。

まずはurls.pyにコメント作成用のurlを追加します。

from django.urls import path
from . import views

app_name = 'blog'

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

次にアプリ内にforms.pyを作成して、コメント作成用のフォームを定義しましょう。

from django import forms
from .models import Comment


class CommentCreateForm(forms.ModelForm):
    """コメント投稿フォーム"""

    class Meta:
        model = Comment
        fields = ('text',)

次にビューの編集をします。

from django.views import generic
from .models import Post, Comment
from .forms import CommentCreateForm  # 追加
from django.shortcuts import redirect, get_object_or_404 # 追加


class PostList(generic.ListView):
    model = Post
    template_name = 'blog/index.html'


class PostDetail(generic.DetailView):
    model = Post
    template_name = 'blog/post_detail.html'

    # 追加
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # テンプレートにコメント作成フォームを渡す
        context['comment_form'] = CommentCreateForm

        return context


# 追加
class CommentCreate(generic.CreateView):
    """
    記事へのコメント作成ビュー
    ページは表示されないが、コメントを作成するために使用
    """
    model = Comment
    form_class = CommentCreateForm

    def form_valid(self, form):
        post_pk = self.kwargs.get('pk')
        post = get_object_or_404(Post, pk=post_pk)

        comment = form.save(commit=False)
        comment.target = post
        comment.save()

        return redirect('blog:post_detail', pk=post_pk)

次にpost_detail.htmlを編集します。

先ほど定義したフォーム内のaction属性に追加したcomment_createのurlを指定します。

{% extends "blog/base.html" %}
{% block content %}
<h2>記事詳細ページです</h2>
<div class="title">
  {{ post.title }}
</div>
<div class="text">
  {{ post.text }}
</div>
<div class="comment">
  <h2>コメント</h2>
  {% for comment in post.comment_set.all %}
  <div class="comment-text">
    <p>{{ comment.text }}</p>
  </div>
  {% endfor %}
</div>
<!-- 追加 -->
<div class="comment-form">
  <h2>コメント投稿</h2>
  <!-- コメント作成用のURLを渡す -->
  <form action="{% url 'blog:comment_create' post.pk %}" method="post">
    {% csrf_token %}
    {{ comment_form}}
    <div>
      <button type="submit">送信</button>
    </div>
  </form>
</div>
{% endblock %}
{% block extrahead %}
<style media="screen">
  .title {
    font-size: 2rem;
  }

  .text {
    margin-top: 30px;
  }

  .comment {
    margin-top: 30px;
  }

  .comment-text {
    margin-top: 20px;
  }

  /*追加*/
  label {
    display: block;
  }
</style>
{% endblock %}

フォームの送信が成功すると、コメント作成用のビューが呼ばれて、保存処理がされる、ということになります。

このようにして送信をしますと…

ページ遷移なしでコメントが追加されました。

Twitter Share