今回はコメント機能の実装について備忘録を残していきます。
当方初心者エンジニアですが、自分のため、そしてこれを見てくれた方のお役に立てればと思います。
これから一部コードを提示しながら説明していきますが、全体のコードをご覧になりたい場合はGithubにありますので、そちらを参照して頂けますと幸いです。
完成イメージ
実装結果はこちらになります。一般的なコメント欄となります。
トップの記事リスト(list.html)から選択した記事詳細(detail.html)の下に付けてみました。
コメントする場所(comment_form.html)は記事詳細(detail.html)に『コメントする』というボタンを追加しましたので、そこから遷移できるようにしました。
名前(デフォルトは名無し)、本文を書いて『投稿する』をポチっと押せば投稿完了です。
『キャンセル』を押せば記事詳細(detail.html)に戻ります。
CSSは神フレームワーク『BootStrap』を使用しました。
いつかオシャレデザインを自分で実装できるようにしたいよ(遠い目)
……ま、まあとにかく。どう実装していったかを下記に記していきます。
参考サイト
下記サイトを放浪しながら構築していきました。いつもお世話になっております。
ほぼ参考サイトのロジックで実装させて頂きました。
Narito Blog 様
知識の枝 様
①モデル
まずはモデルを定義していきます。
当然ですが、コメント機能はブログ記事があってこそ成立します。なのでまずはブログ記事のモデルを用意します。
#ブログ記事モデル
#モデル名が『BlogModel』となっておりますが、『Blog』とか、『Article』とかにした方が良いですね(汗)
class BlogModel(models.Model):
title = models.CharField(max_length=100, verbose_name='タイトル')
content = MDTextField(verbose_name='内容')
postdate = models.DateField(auto_now_add=True, verbose_name='投稿日')
category = models.CharField(
max_length= 50,
choices= CATEGORY,
verbose_name= 'ジャンル'
)
tag = models.ManyToManyField(Tag, blank=True)
なんか色々とフィールドを設定してますが、とりあえずtitile, content, postdateフィールドさえあれば問題ないかと。
このブログ記事モデルが出来ましたら、次はお待ちかねのコメントモデルを定義していきます。
#コメントモデル
class Comment(models.Model):
user_name = models.CharField('名前', max_length=255, default='名無し')
message = models.TextField('本文')
target = models.ForeignKey(BlogModel, on_delete=models.CASCADE, verbose_name='対象記事')
created_at = models.DateTimeField('作成日', default=timezone.now)
def __str__(self):
return self.message[:20]
さて、色々とフィールドが設定されておりますね。
user_nameは名前、messageはコメント本文,created_atはコメント投稿日時となります。
そして大事なのはtargetフィールドです。
該当するブログ記事は一つだけですが、それに対するコメントはもちろん複数ありますよね。
それを実現するにはForeignKeyを使用して、「1対多」の関係を構築する必要があります。
なので引数には先ほど定義したBlogModelを設定します。
これでモデルの定義は完了です。あとは忘れないよう、マイグレーションです。
python manage.py makemigrations
python manage.py migrate
②フォーム
ふう、ようやくモデルが完成しましたね。
というわけで、お次はフォームの設定をしていきます。
モデルの定義はOKですが、これだけでなくフォームも定義する必要があります。
まずはアプリフォルダ直下(models.pyやviews.pyと同じ階層)にforms.pyファイルを作成します。
作成後、以下のように記述していきます。
from django import forms
from .models import Comment
#コメント投稿フォーム
class CommentCreateForm(forms.ModelForm):
class Meta:
model = Comment
exclude = ('target', 'created_at')
まずは必要なライブラリをインポートしていきます。
コメントフォームを作成しますので、先ほど定義したコメントモデルもインポートします。
メタデータに exclude = ('target', 'created_at')を記述していますが、これはフォーム画面に表示させないよう除外しております。
どの記事に紐づくかのtarget、および作成日であるcreated_atです。
③ビュー
ここまでモデルとフォームの定義が完了しました。
次はビューを作っていきます。
from django.shortcuts import redirect, get_object_or_404
from django.views.generic import DetailView
from django.views.generic.edit import CreateView
from .models import BlogModel, Comment
from .forms import CommentCreateForm
from django import forms
#ブログ記事詳細ページのビュー
class BlogDetail(DetailView):
template_name = 'detail.html'
model = BlogModel
#コメント投稿ページのビュー
class CommentView(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(BlogModel, pk=post_pk)
comment = form.save(commit=False)
comment.target = post
comment.save()
return redirect('blogapp:detail', pk=post_pk)
#htmlテンプレートに渡すデータを定義
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['post'] = get_object_or_404(BlogModel, pk=self.kwargs['pk'])
return context
クラスベースビューとなります。こちらはほぼ参考サイトと同じですが、一部書き換えている部分もあります。
CommentViewのtemplate_name = 'comment_form.html'ですが、こちらのテンプレートは後ほど作成します。それとビューの作成に必要なライブラリのインポートも忘れないようにします。
④ルーティング
urls.pyの設定も欠かさず行っていきます。アプリ直下にurls.pyを作成し、以下の通り記述します。
from django.urls import path
from .views import BlogDetail, CommentView
app_name = 'blogapp'
urlpatterns = [
path('detail/<int:pk>', BlogDetail.as_view(), name='detail'),
path('comment/create/<int:pk>/', views.CommentView.as_view(), name='comment_create'),
]
⑤テンプレート
では、いよいよ表示するためのテンプレートを作っていきます。
今回はブログ記事詳細ページであるdetail.htmlの一番下部分にコメント欄を配置するため、以下のように記述していきます。
<h3 class="comment-title">コメント</h3>
<!--コメント-->
{% for comment in object.comment_set.all %}
<div class="comment-list">
<div class='border-bottom'>{{ comment.user_name }} {{ comment.created_at }}</div>
<!--改行しないようlinebreaksを設定,URL要素に<a>要素を設定-->
<div class='mt-2'>{{ comment.message | linebreaks | urlize }}</div>
</div>
{% empty %}
<p>コメントはありません</p>
{% endfor %}
上記を記述することにより、冒頭の完成イメージで見せたコメント欄が表示されます。
次は肝心なコメント投稿ページを作成します。
<div class="container col-lg-6 offset-lg-3">
<!---記事タイトルを表示-->
<h2 class="titleline"><span>{{ post.title }}</span></h2>
<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 'blogapp:detail' post.pk %}">キャンセル</a>
</div>
</form>
</div>
{% for field in form %}はフォームの各フィールドを取り出します。今回の場合はuser_name(名前)、message(本文)を入力するフォームが表示されるようになります。
さいごに
こんな感じでコメント機能を実装しました。
本当は返信機能も実装したかったところですが、それはまた別の機会に。。。今はブログ記事詳細のページに目次機能を実装するため目下努力中であります。
結構ざっくばらんに書き綴りましたが、何か不備がございましたらご連絡ください。
それでは、また何かありましたら備忘録を書こうと思います。