宿題:コメントモデルを作ろう

現在、ポストモデルしかありません。 あなたの読者からいくつかのフィードバックを受け取り、彼らにコメントさせるのはどうでしょうか?

ブログにコメントモデルを作る

blog/models.pyを開き、このコードをファイルの最後に追加しましょう:

class Comment(models.Model):
    post = models.ForeignKey('blog.Post', on_delete=models.CASCADE, related_name='comments')
    author = models.CharField(max_length=200)
    text = models.TextField()
    created_date = models.DateTimeField(default=timezone.now)
    approved_comment = models.BooleanField(default=False)

    def approve(self):
        self.approved_comment = True
        self.save()

    def __str__(self):
        return self.text

各フィールドタイプの意味を思い出す必要がある場合は、チュートリアルのDjangoモデルの章に戻ることができます。

拡張版チュートリアルでは、新しいフィールドタイプが登場します:

  • models.BooleanField - こちらはtrue/falseの値が入るフィールドです。

models.ForeignKeyrelated_name引数は、ポストモデルの中からコメントにアクセスできるようにします。

データベースにモデルのテーブルを作る

今度は、コメントモデルをデータベースに追加します。 これを行うには、モデルを変更したことをDjangoに伝えなければなりません。 コマンドラインに python manage.py makemigrations blogと入力してください。 次のような出力が表示されます:

(myvenv) ~/djangogirls$ python manage.py makemigrations blog
Migrations for 'blog':
  0002_comment.py:
    - Create model Comment

このコマンドがblog/migrations/ディレクトリに別のマイグレーションファイルを作成したことがわかります。 コマンドラインで python manage.py migrate blogとタイプして変更を適用する必要があります。 出力は次のようになります:

    (myvenv) ~/djangogirls$ python manage.py migrate blog
    Operations to perform:
      Apply all migrations: blog
    Running migrations:
      Rendering model states... DONE
      Applying blog.0002_comment... OK

コメントモデルは今データベースに存在します! 管理画面でアクセスできたら素敵ですよね?

管理画面にコメントモデルを登録する

管理画面にコメントモデルを登録するため、blog/admin.pyファイルに以下の行を追加してください:

admin.site.register(Comment)

追加する位置は、この行の直下です:

admin.site.register(Post)

コメントモデルをファイルの先頭でインポートすることも忘れないようにしてください:

from django.contrib import admin
from .models import Post, Comment

admin.site.register(Post)
admin.site.register(Comment)

コマンドラインでpython manage.py runserverを入力し、あなたのブラウザで http://127.0.0.1:8000/admin/ に行くと、コメントリストにアクセスすることができ、コメントの追加と削除の機能が使えるようになります。新しいコメント機能で遊んでみましょう!

コメントを見えるようにする

blog/templates/blog/post_detail.htmlというファイルを開き、{% endblock %}タグの前に、以下の行を追加してください:

<hr>
{% for comment in post.comments.all %}
    <div class="comment">
        <div class="date">{{ comment.created_date }}</div>
        <strong>{{ comment.author }}</strong>
        <p>{{ comment.text|linebreaks }}</p>
    </div>
{% empty %}
    <p>No comments here yet :(</p>
{% endfor %}

これで投稿の詳細ページでコメントを見ることができます。

でも、コメントをもう少し良く見せられるかもしれませんね。 static/css/blog.cssファイルの一番下にいくつかのCSSを追加しましょう:

.comment {
    margin: 20px 0px 20px 20px;
}

投稿一覧ページでコメントについて、訪問者に知らせることもできます。 blog/templates/blog/post_list.htmlファイルを開き、次の行を追加します:

<a href="{% url 'post_detail' pk=post.pk %}">Comments: {{ post.comments.count }}</a>

追加したら、テンプレートは次のようになります:

{% extends 'blog/base.html' %}

{% block content %}
    {% for post in posts %}
        <div class="post">
            <div class="date">
                {{ post.published_date }}
            </div>
            <h1><a href="{% url 'post_detail' pk=post.pk %}">{{ post.title }}</a></h1>
            <p>{{ post.text|linebreaksbr }}</p>
            <a href="{% url 'post_detail' pk=post.pk %}">Comments: {{ post.comments.count }}</a>
        </div>
    {% endfor %}
{% endblock content %}

読者がコメントを書けるようにする

今は私たちのブログでコメントを見ることができますが、追加することはできません。 それを変えましょう!

blog/forms.pyを開き、以下の行をファイルの最後に追加します:

class CommentForm(forms.ModelForm):

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

コメントモデルをインポートすることを忘れないでください。以下の行を変更して:

from .models import Post

このようにします:

from .models import Post, Comment

今度は、blog/templates/blog/post_detail.htmlを開き、{% for comment in post.comments.all %}の前に以下の行を追加します:

<a class="btn btn-default" href="{% url 'add_comment_to_post' pk=post.pk %}">Add comment</a>

もし投稿詳細ページに行くと、以下のエラーが表示されます:

NoReverseMatch

私たちはそれを修正する方法を知っています! blog/urls.pyを開き、このパターンをurlpatternsに追加してください:

path('post/<int:pk>/comment/', views.add_comment_to_post, name='add_comment_to_post'),

ページを更新したら、また違うエラーが出てきます!

AttributeError

こちらのエラーを修正するために、blog/views.pyに以下のビューを追加してください:

def add_comment_to_post(request, pk):
    post = get_object_or_404(Post, pk=pk)
    if request.method == "POST":
        form = CommentForm(request.POST)
        if form.is_valid():
            comment = form.save(commit=False)
            comment.post = post
            comment.save()
            return redirect('post_detail', pk=post.pk)
    else:
        form = CommentForm()
    return render(request, 'blog/add_comment_to_post.html', {'form': form})

CommentFormをファイルの先頭でインポートすることを忘れないでください:

from .forms import PostForm, CommentForm

今、詳細投稿ページで”Add Comment”というボタンが表示されています。

AddComment

しかし、そのボタンをクリックしたら、次のように表示されます:

TemplateDoesNotExist

表示されたエラーが伝えているように、テンプレートは存在していないので、blog/templates/blog/add_comment_to_post.htmlという新しいテンプレートを作り、以下のコードを追加してください:

{% extends 'blog/base.html' %}

{% block content %}
    <h1>New comment</h1>
    <form method="POST" class="post-form">{% csrf_token %}
        {{ form.as_p }}
        <button type="submit" class="save btn btn-default">Send</button>
    </form>
{% endblock %}

わーい! 今あなたの読者は、ブログの投稿をどう思っているかを知らせることができます!

コメントを管理する

すべてのコメントを表示する必要はありません。 ブログの所有者として、あなたはコメントを承認または削除する選択肢をおそらく望むでしょう。 それについて何かしましょう。

blog/templates/blog/post_detail.htmlを開き、以下の行を:

{% for comment in post.comments.all %}
    <div class="comment">
        <div class="date">{{ comment.created_date }}</div>
        <strong>{{ comment.author }}</strong>
        <p>{{ comment.text|linebreaks }}</p>
    </div>
{% empty %}
    <p>No comments here yet :(</p>
{% endfor %}

このように変更してください:

{% for comment in post.comments.all %}
    {% if user.is_authenticated or comment.approved_comment %}
    <div class="comment">
        <div class="date">
            {{ comment.created_date }}
            {% if not comment.approved_comment %}
                <a class="btn btn-default" href="{% url 'comment_remove' pk=comment.pk %}"><span class="glyphicon glyphicon-remove"></span></a>
                <a class="btn btn-default" href="{% url 'comment_approve' pk=comment.pk %}"><span class="glyphicon glyphicon-ok"></span></a>
            {% endif %}
        </div>
        <strong>{{ comment.author }}</strong>
        <p>{{ comment.text|linebreaks }}</p>
    </div>
    {% endif %}
{% empty %}
    <p>No comments here yet :(</p>
{% endfor %}

comment_removecomment_approveパターンに一致するURLがまだないので、 NoReverseMatchが表示されます。

こちらのエラーを修正するため、以下のURLパターンをblog/urls.pyに追加してください:

path('comment/<int:pk>/approve/', views.comment_approve, name='comment_approve'),
path('comment/<int:pk>/remove/', views.comment_remove, name='comment_remove'),

さて、あなたにはAttributeErrorが見えています。このエラーを修正するために、以下のビューをblog/views.pyに追加してください:

@login_required
def comment_approve(request, pk):
    comment = get_object_or_404(Comment, pk=pk)
    comment.approve()
    return redirect('post_detail', pk=comment.post.pk)

@login_required
def comment_remove(request, pk):
    comment = get_object_or_404(Comment, pk=pk)
    comment.delete()
    return redirect('post_detail', pk=comment.post.pk)

ファイルの先頭に Commentをインポートする必要があります:

from .models import Post, Comment

すべて動作します!私たちにできる小さな微調整があります。投稿一覧ページ(の投稿の下)には、現在、投稿が受け取ったすべてのコメントの数が表示されます。承認された コメントの数を表示するように変更しましょう。

これを修正するには、 blog/templates/blog/post_list.htmlを開き、次の行を:

<a href="{% url 'post_detail' pk=post.pk %}">Comments: {{ post.comments.count }}</a>

下記のように変更してください:

<a href="{% url 'post_detail' pk=post.pk %}">Comments: {{ post.approved_comments.count }}</a>

最後に、このメソッドを blog/models.pyPostモデルに追加してください:

def approved_comments(self):
    return self.comments.filter(approved_comment=True)

これでコメント機能は終了です! おめでとう! :-)

results matching ""

    No results matching ""