投稿日: | 最終更新日:
コメント投稿に対してコメント投稿するページを作る(Django)
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のみとします。
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のプロパティでアクセスできます。
- Python 114
- 制作 54
- RaspberryPi 41
- Django 40
- WordPress 40
- Linux 27
- VPS 22
- JavaScript 21
- PHP 20
- HTML・CSS 19
- AWS 16
- 仮想環境 15
- レスポンシブデザイン 13
- マイコン 11
- WEB全般 11
- 動画製作 9
- Webサービス 8
- 統合開発環境 8
- 機械学習 8
- PyCharm 7
- jQuery 7
- AfterEffects 7
- 起業・設立 7
- Django REST framework 6
- C# 6
- デザイン 6
- SEO 6
- pydata 6
- Visual Studio 5
- 数学 5
- 携帯サイト 5
- heroku 5
- Mac 5
- illustrator 5
- node.js 5
- Anaconda 5
- Nginx 4
- Jupyter Notebook 4
- インフラ 4
- Google Colaboratory 4
- symfony 4
- Webスクレイピング 3
- photoshop 3
- Go言語 3
- PC 3
- ツール 3
- Docker 3
- facebook 3
- 作業効率化 3
- データベース 3
- Cloud9 3
- コマンド 2
- micro:bit 2
- Kali Linux 2
- Webサーバー 2
- MariaDB 2
- ドローン 2
- コンテナ 2
- DaVinci Resolve 2
- ネットワーク 2
- Java 2
- movie 2
- PCDJ 2
- 音楽 2
- XSERVER 2
- Ansible 1
- Vue.js 1
- JSON 1
- Bootstrap 1
- バージョン管理システム 1
- SSL 1
- S3 1
- ムームードメイン 1
- ネットワーク 1
- アニメーション 1
- D3.js 1
- Rhino 1
- アニメ 1
- git 1
- windows 1
- アクセス解析 1
- スマートフォン 1
- アフィリエイトノウハウ 1
- 知識 1
- TypeScript 1
- 役立つ本・書籍 1
- データサイエンス 1
- ESP32 1
- AI 1
- ownCloud 1
- API 1