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
にどの記事に対するコメントか、という情報を保存します。
アプリ内の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 %}
フォームの送信が成功すると、コメント作成用のビューが呼ばれて、保存処理がされる、ということになります。
このようにして送信をしますと…
ページ遷移なしでコメントが追加されました。