naritoブログ

このブログはPython/DjangoとBootstrap4で作成されました
Githubのソースコード

Djangoで、コメントの承認機能と、コメントへの返信機能

約393日前 2017年5月28日10:06
プログラミング関連
Bootstrap4 Django Python
まずはこのように、記事の一覧画面です。


続きを読む、を押すと記事の詳細画面です。
タイトル、本文のほか、コメントフォームへのリンクとコメント一覧があります。


コメントする、を押すとこのようなページに。


コメントしても、承認されるまではちゃんと表示されません。


管理画面で承認(あるフィールドをTrueに変更するだけ)すると、表示されます。


返信する、を押すととまた画面に。こちらも承認されないとちゃんと表示されません。
承認すると、元コメントの少し右側に返信コメントが表示されます。




project/urls.py


appのurls.pyをincludeします。
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('app.urls')),
]


app/urls.py


一覧、詳細、コメント、返信コメントです。
from django.urls import path
from . import views

app_name = 'app'

urlpatterns = [
    path('', views.PostListView.as_view(), name='post_list'),
    path('detail/<int:pk>/',
        views.PostDetailView.as_view(), name='post_detail'),
    path('comment/<int:pk>/',
        views.CommentView.as_view(), name='comment'),
    path('reply/<int:pk>/',
        views.ReplayView.as_view(), name='replay'),
]


app/models.py


記事、コメント、返信コメントのモデルがあります。
コメントの承認機能は単純で、is_publick = models.BooleanField(default=False)です。
あとはtemplate側で、Falseなら「承認されていない」等と表示し、Trueにされていればそのまま表示する、という処理にするだけです。
from django.db import models


class Post(models.Model):
    """記事."""
    title = models.CharField(max_length=255)
    text = models.TextField()

    def __str__(self):
        return self.title


class Comment(models.Model):
    """コメント."""
    name = models.CharField(max_length=255, blank=True)
    text = models.TextField()
    target = models.ForeignKey(Post, on_delete=models.CASCADE)
    is_publick = models.BooleanField(default=False)

    def __str__(self):
        return self.name


class Reply(models.Model):
    """返信コメント."""
    name = models.CharField(max_length=255, blank=True)
    text = models.TextField()
    target = models.ForeignKey(Comment, on_delete=models.CASCADE)
    is_public = models.BooleanField(default=False)

    def __str__(self):
        return self.name



app/views.py


ビューも割とシンプルな作りになったと思います。
from django.shortcuts import redirect, get_object_or_404
from django.views import generic
from .models import Post, Comment, Reply


class PostListView(generic.ListView):
    """/ でアクセス記事一覧."""
    model = Post


class PostDetailView(generic.DetailView):
    """/detail/post_pk でアクセス。記事詳細."""
    model = Post


class CommentView(generic.CreateView):
    """/comment/post_pk コメント投稿."""
    model = Comment
    fields = ('name', 'text')
    template_name = 'app/comment_form.html'

    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)


class ReplyView(generic.CreateView):
    """/reply/comment_pk 返信コメント投稿."""
    model = Reply
    fields = ('name', 'text')
    template_name = 'app/comment_form.html'

    def form_valid(self, form):
        comment_pk = self.kwargs['pk']
        comment = get_object_or_404(Comment, pk=comment_pk)

        # 紐づくコメントを設定する
        reply = form.save(commit=False)
        reply.target = comment
        reply.save()

        # 記事詳細にリダイレクト
        return redirect('post_detail', pk=comment.target.pk)


app/base.html


Bootstrap4を利用しています。
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <meta http-equiv="x-ua-compatible" content="ie=edge">
 
        <!-- Bootstrap CSS -->
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css">
    </head>
    <body>
        <div class="container mt-5">
            {% block content %}{% endblock %}    
        </div>

        <!-- jQuery first, then Tether, then Bootstrap JS. -->
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.2.0/js/tether.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js"></script>
    </body>
</html>



app/post_list.html


記事一覧
{% extends "app/base.html" %}
{% block content %}
<h1>記事一覧</h1>
<hr>

{% for post in post_list %}
    <h2>{{ post.title }}</h2>
    <p><a href="{% url 'app:post_detail' post.pk %}">続きを読む</a></p>
{% endfor %}

{% endblock %}


app/post_detail.html


記事詳細。
{% if comment.is_public %}
のようにして、承認されているかをチェックしています。
返信コメントの位置は、単純に<div style="margin-left:100px;">で右にずらしています。
{% extends "app/base.html" %}
{% block content %}
<h1>記事の詳細</h1>
<hr>

<h2>{{ post.title }}</h2>
<p>{{ post.text }}</p>
<hr>

<h2>コメント一覧</h2>
<a href="{% url 'app:comment' post.pk %}">コメントする</a>
<hr>
{% for comment in post.comment_set.all %}
    {% if comment.is_public %}
        名前:{{ comment.name }}<br>
        テキスト:{{ comment.text }}<br>
        <a href="{% url 'app:reply' comment.pk %}">返信する</a><br>
    
        {% for reply in comment.reply_set.all %}
        <div style="margin-left:100px;">
            {% if reply.is_public %}
                名前:{{ reply.name }}<br>
                テキスト:{{ reply.text }}<br>       
            {% else %}
                <p>まだ承認されていません。(返信コメント)</p>
            {% endif %}
        </div>
        {% endfor %}

    {% else %}
        <p>まだ承認されていません。(コメント)</p>
    {% endif %}
    <hr>
{% endfor %}
{% endblock %}


app/comment_form.html


コメント、返信コメント投稿用フォーム
{% extends "app/base.html" %}
{% block content %}
<h1>コメントフォーム</h1>
<hr>

<form action="" method="POST">
    {{ form.as_p }}
    {% csrf_token %}
    <button type="submit">送信</button>
</form>
{% endblock %}
名無し 約32日前 2018年5月24日11:00 返信する
なりとさんこんにちは。いつも楽しく拝見しています。
記事を読んでいくつか気になった点があったので、おせっかいながら指摘させていただきます。
記事内でclass Recommentとされていますが、おそらくclass Replyという表現のほうが適切だろうと思います。
確認したところYouTubeのAPIなんかもこの表現でした。
あとis_publickのつづりが間違っていて、is_publicが正しいつづりです。
これからも楽しい記事を期待しています。
どうでもいいですが最近おふざけ要素が少なくて少し寂しく思ったりもしています(笑)
なりと 約32日前 2018年5月24日12:13
わざわざありがとうございます。
これは酷い名前ですね。修正しておきます。
おふざけ要素も考えておきます。ちょっと図や絵での説明も多くしていこうと思ってるので、うまく使おうと思います。