ホーム >  Python > Django >  コメント投稿に対してコメント投稿するページを作る(Django)

投稿日:   |  最終更新日:

コメント投稿に対してコメント投稿するページを作る(Django)

DjangoPython

Djangoでコメントに対する返信機能を作成します。

コメント投稿に対してコメント投稿

SNSなどでよく見かける機能です。コメント一覧があり、1つのコメントに対して返信します。なおページ遷移などは行わず、コメントに対する返信を同ページ内で行います。Youtubeやinstagramがその代表例です。

準備

当サイトでは、Vagrantでゲスト環境(仮想環境)を作ってDjangoを動かいていますが、それ以外の環境でも操作方法はだいたい同じです。

①Djangoプロジェクトを生成します。

VagrantのDjangoで作るWebアプリケーション(その1 プロジェクト生成)

②Djangoアプリケーションを作ります。

VagrantのDjangoで作るWebアプリケーション(その2 プロジェクトの初期設定)

③投票アプリのモデルを定義します。

投票アプリを作る その1モデルの定義、管理サイトの表示(Django)

④Djangoの管理ページからモデルを編集します。

投票アプリを作る その2管理サイト上のモデル編集(Django)

⑤DjangoのViewとTemplateの連携します。

投票アプリを作る その3モデルとView・Templateの連携(Django)

環境

OS CentOS 7.1.1503
Python 3.6.5
pyenv 1.1.3-5-g7dae197
MariaDB 5.5.52-1.el7
Django 2.0.3

コメント返信アプリ概要

①以下のようなDjangoアプリ構成を作ります。プロジェクト名が「pj1」で、アプリケーション名が「users」です。プロジェクトの設定は済ませたものとします。赤字のファイルは、今回変更する部分です。

  • pj1/
  • pj1/
  • __init__.py
  • settings.py
  • urls.py
  • wsgi.py
  • users/
  • __init__.py
  • admin.py
  • apps.py
  • urls.py
  • views.py
  • models.py
  • templates/app/
  • base.html
  • Comment_list.html

modelsの追加・データのInsert

modelsにモデルを追加します。コメントがあり、それに紐づくコメントがあります。

①usersディレクトリのviews.pyに以下の内容を記述します。

from django.db import models


class Comment(models.Model):
    text = models.TextField('日記')
    created_at = models.DateTimeField(auto_now_add=True)


class SubComment(models.Model):
    text = models.CharField('コメント', max_length=300)
    target = models.ForeignKey(Comment, on_delete=models.CASCADE, verbose_name='紐づく日記')
    created_at = models.DateTimeField(auto_now_add=True)

②以下のコマンドでマイグレーションを行います。

cd pj1
python manage.py makemigrations
python manage.py migrate

③最初のメインのコメントを1件つくります。以下のコマンドを入力してインタラクティブシェルを起動します。

python manage.py shell

④以下の記述でコメントを1件作ります。

from users.models import Comment
Comment_1 = Comment()
Comment_1.text = '1件目のコメント'
Comment_1.save()

登録が成功したかどうか確認します。

Comment_1.text
'1件目のコメント'

urlの設定

Djangoへのアクセス時には最初にROOT_URLCONF(デフォルトではproject/urls.py、今回の例ではpj1/urls.py)を確認します。そして、どのviewを呼び出すかを確認します。urlを設定すると、結果をブラウザから確認できるようになります。

まず、usrs(アプリケーション)ディレクトリのurls.pyを記述した後、pj1(プロジェクトディレクトリ)のurls.pyも記述します。

  • pj1/
  • pj1/
  • urls.py
  • 〜省略〜
  • users/
  • urls.py
  • 〜省略〜
  • templates/

①usersディレクトリのurls.pyに以下の内容を記述します。

from django.conf.urls import url

from . import views


urlpatterns = [
    url(r'^$', views.index, name='index'),  # urlとviewの結びつけ
    url("^comment_create/(?P<pk>\d+)/$",views.create_comment,name='comment_create'), # 複数のform要素
]

これで「users.views.index」とusersのurlが接続されました。しかしこの段階では、「users.urls」はroot_urlではないためまだブラウザから確認することはできません。

②pj1ディレクトリのurls.pyに以下の内容を記述します。

from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'^', include('users.urls', namespace = 'users')),  # urlと他のurlpatternsとの結びつけ
]

formsの追加

①usersディレクトリのforms.pyに以下の内容を記述します。

from django import forms
from .models import SubComment

class CommentCreateForm(forms.ModelForm):

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

targetをfieldsに含めないようにしておきましょう。紐づくSubCommentというプルダウンが表示されてしまいます。

viewの追加

①usersディレクトリのviews.pyに以下の内容を記述します。

from django.views.decorators.http import require_POST
from django.shortcuts import render, redirect, get_object_or_404
from .forms import CommentCreateForm
from .models import Comment

def index(request):

    context = {
        'Comment_list.html': Comment.objects.all(),
        'form': CommentCreateForm(),
    }
    return render(request, 'app/Comment_list.html', context)


@require_POST
def create_comment(request, pk):
    form = CommentCreateForm(request.POST)
    if form.is_valid():
        subcomment = form.save(commit=False)
        subcomment.target = get_object_or_404(Comment, pk=pk)
        subcomment.save()
        return redirect('users:index')

    context = {
        'Comment_list': Comment.objects.all(),
        'form': form,
    }
    return render(request, 'app/Comment_list.html', context)

解説

def index(request):

    context = {
        'Comment_list': Comment.objects.all(),
        'form': CommentCreateForm(),
    }
    return render(request, 'app/Comment_list.html', context)

indexビューは、Comment(メイン)の一覧とコメント作成用フォームをテンプレートへ渡します。

@require_POST
def create_comment(request, pk):
    form = CommentCreateForm(request.POST)
    if form.is_valid():
        subcomment = form.save(commit=False)
        subcomment.target = get_object_or_404(Comment, pk=pk)
        subcomment.save()
        return redirect('users:index')

    context = {
        'Comment_list': Comment.objects.all(),
        'form': form,
    }
    return render(request, 'app/Comment_list.html', context)

create_commentビューは、コメント(サブ)を送信するとき呼ばれるビューです。ビューにはコメント(メイン)のpkが渡されるので、それをコメントに設定して保存をします。

@require_POST

「@require_POST」デコレーターを利用して、ビューにアクセスできるHTTPメソッドはPOSTのみとします。

Pythonのデコレーターで関数を修飾してみよう

        comment = form.save(commit=False)
        comment.target = get_object_or_404(Comment, pk=pk)
        comment.save()

「form.save(commit=False)」の「commit=False」でsaveメソッドを呼び出します。データベースに保存する前のモデルインスタンスを取得します。

その後は自由に属性の設定ができ、「comment.target = get_object_or_404(Comment, pk=pk)」を設定したら、saveメソッドを呼び出し実際に保存します。

「get_object_or_404」はpkを利用して、データベースからレコードを取得します。第一引数にComment(メイン)を指定して取得します。取得できなかった場合は、Http404が発生します。


templateの作成

base.html

ペースのテンプレートとして、Bootstrap4のスターターテンプレートを利用します。

<!doctype html>
<html lang="ja">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">

    <title>一言日記</title>
  </head>
  <body>
    <div class="container mt-5">
        {% block content %}{% endblock %}
    </div>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
    {% block extrajs %}{% endblock %}
  </body>
</html>

Comment_list.html

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

{% block content %}
    <h1 class="mb-5">コメント</h1>
    {% for comment in Comment_list %}
        {{ comment.text | linebreaksbr }} <span class="text-muted">{{ comment.created_at }}</span>

        <!-- コメント一覧と、コメントフォーム -->
        <div class="mt-3 ml-5">
            <!-- そのメインコメントへのコメントフォーム -->
            <form action="{% url 'app:comment_create' diary.pk %}" method="POST">
                {{ form }}
                <button type="submit">送信</button>
                {% csrf_token %}
            </form>

            <!-- そのメインコメントに紐づいたサブコメントを取り出す -->
            {% for comment in comment.subcomment_set.all %}
                {{ comment.text }} <span class="text-muted">{{ comment.created_at }}</span><br>
            {% endfor %}
        </div><!-- コメント一覧・コメントフォーム終わり -->

        <hr>
    {% endfor %}
{% endblock %}

解説

    <h1 class="mb-5">コメント</h1>
    {% for comment in Comment_list %}

〜(省略)〜

    {% endfor %}

「{% for diary in Comment_list %}」の部分でコメント(メイン)を1つずつ取り出します。

このforループの中で<form>要素を定義しているため、日記が2件ならform要素は2つ作られ、10件なら10個作られます。

            <form action="{% url 'app:comment_create' diary.pk %}" method="POST">
                {{ form }}
                <button type="submit">送信</button>
                {% csrf_token %}
            </form>

各フォームは<form action=”/comment/4/” method=”POST”>のようになります。4の部分は日記のpkで、create_commentビューにpk引数として渡されるものです。

type=”submit”なボタンは、あくまでそれが属するフォーム要素のデータだけを送信します。

            <!-- そのメインコメントに紐づいたサブコメントを取り出す -->
            {% for comment in comment.subcomment_set.all %}
                {{ comment.text }} <span class="text-muted">{{ comment.created_at }}</span><br>
            {% endfor %}
        </div><!-- コメント一覧・コメントフォーム終わり -->

「comment.subcomment_set.all」は、Commentモデルと1対多で結ばれるSubCommentを取得します。

関連元モデル.関連先モデル名_setのプロパティでアクセスできます。



トラックバック用のURL
プロフィール

名前:イワサキ ユウタ 職業:システムエンジニア、ウェブマスター、フロントエンドエンジニア 誕生:1986年生まれ 出身:静岡県 特技:ウッドベース 略歴 20

最近の投稿
人気記事
カテゴリー
広告