見出し画像

実践Django宿題の回答例

実践Django Pythonによる本格Webアプリケーション開発 3.2 LTS対応(芝田 将 著)内にて、chapter1.7宿題の回答例。

作成手順

  1. コメントDB作成(構成は書籍の通り)

  2. snippet_detail.html下部にコメント一覧を作成

  3. コメント一覧の下にコメント投稿フォーム設置

  4. 新規URL(コメント保存処理用)と保存用のviewを用意

  5. 保存後、detail画面へリダイレクト処理
    (=4のURLは存在するが表示されないのでhtml不要)

  6. htmlのフォームを調整したりなんやかんや

最初、detailViewでPOST判定でif分岐させようとしたが上手に書けず、別ページ用意した流れ。cssはガン無視したので綺麗な形ではない。

1.DB作成


これはもうそのまま。

[snippets/models.py]
from django.conf import settings
from django.db import models

class Snippet(models.Model):
    title = models.CharField('タイトル', max_length=128)
    code = models.TextField('コード', blank=True)
    description = models.TextField('説明', blank=True)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL,
            verbose_name='投稿者',
            on_delete = models.CASCADE)
    created_at = models.DateTimeField('投稿日', auto_now_add=True)
    updated_at = models.DateTimeField('更新日', auto_now=True)

    def __str__(self):
        return self.title

#ここから追記
class Comment(models.Model):
    text = models.TextField('コメント', blank=False)
    commented_at = models.DateTimeField('投稿日', auto_now_add=True)
    commented_by = models.ForeignKey(settings.AUTH_USER_MODEL,
            verbose_name = '投稿者',
            on_delete = models.CASCADE)
    commented_to = models.ForeignKey(Snippet,
            verbose_name = 'スニペット',
            on_delete = models.CASCADE)

#もし、管理ページで表示を変えたければ
    def __str__(self): #str型の表示になる、pkで出したい場合はintとかにしておいたらいいんでない?
        return self.text
        #return self.commented_to

これでDBは完成。makemigrations、migrateなどやったらいいと思う。
書籍と違う部分は最後のブロック、def~くらいかな。

2.コメント一覧作成

とりあえずコメントを見れるようにする。見れるかどうか判別しやすくするため、管理画面からコメントを何個か入れておくと楽。

[djangosnippets/snippets/templates/snippets/snippet_detail.html]
{% extends "base.html" %}
{% load pygmentize %}
{% load django_bootstrap5 %}

{% block main %}
{{ snippet.title }} by {{ snippet.created_by.username}}
投稿日:{{ snippet.created_at|date:"DATETIME_FORMAT" }}
{% if user.is_authenticated and snippet.created_by_id == user.id %} 編集 {% endif %} 
{{ snippet.code|pygmentize:"python3" }} 
{{ snippet.description }}

<!-- ここから追記 -->

<br><br>
<h2>コメント</h2>
<div style="background-color:#EDF7FF;">
{% for i in snippet.comment_set.all %}
◆{{ i.text }}
  by {{ i.commented_by }}さん {{ i.commented_at }}
</div>
{% endfor %} 

{% endblock %} 

見た目は以下の通り。わかりにくいのでコメントは背景に青入れした。

コメント表示

当初、viewでコメント用のオブジェクトセットを作成しないと表示できないのかと苦戦。最終的にそんなことせずとも「snippet.comment_set.all」で取り出せた。set_allが肝で、裏側で絞り込みしなくてもDBで紐付けされた対象のものしか出てこない。のだと思う。まだ調べていません。

3.コメントフォーム設置


とりあえず深く考えずにフォームを作成。項目も少ないため下手打ちで良いだろうと踏んだ。

[djangosnippets/snippets/templates/snippets/snippet_detail.html]

〜省略〜

<h2>コメント</h2>
<div style="background-color:#EDF7FF;">
{% for i in snippet.comment_set.all %}
◆{{ i.text }}
  by {{ i.commented_by }}さん {{ i.commented_at }}
</div>
{% endfor %} 

<!-- ここから追加 -->

<form action="" method="POST">
{% csrf_token %}
<h3>コメントを投稿する</h3>
<input type="text" name="text">
<input type="submit" value="コメント">
</form>

<!-- ここまで追加 -->

{% endblock %} 

まだ遷移先を用意していない為、actionは空で良い。ここまでで、先程の画面に入力欄が追加された。ボタンを押しても何も動かんけど。

4.URLとviewの用意

なぜ必要なのか
viewにて、snippet_detail内でPOSTとelseでif分岐させようとしたがうまくいかなかったので。他に良き方法があれば是非知りたい。
用意するもの
URLとそれに紐付いたview

[djangosnippets/snippets/urls.py]

from django.urls import path
from . import views

urlpatterns=[
        path('new/', views.snippet_new, name='snippet_new'),
        path('<int:snippet_id>/', views.snippet_detail, name='snippet_detail'),
        path('<int:snippet_id>/edit/', views.snippet_edit, name='snippet_edit'),
        #ここから追記
        path('<int:snippet_id>/comment/', views.comment_create, name="comment"),
]

まずurls.pyに保存用にurlを用意。comment_createのviewはこのあとすぐ作成する。。

view作る。省略箇所は書籍通り。新規にimportするものもなし。

[djangosnippets/snippets/views.py]

〜省略〜

@login_required
def comment_create(request, snippet_id):
    if request.method == "POST":
        #解説1
        texts = request.POST.getlist('text')
        text = texts[0]
    #解説2
        commented_by = request.user
    #解説3
        idid = Snippet(pk=snippet_id)
        commented_to = idid
        datas = Comment(text=text, commented_by=commented_by, commented_to=commented_to)
        datas.save()
    #手順5のリダイレクト処理
        return redirect('snippet_detail', snippet_id=snippet_id)

※基本POSTでしか飛んでこないのでもっとシンプルでもいいと思う。

解説1
フォームからコメント内容を受け取っているので取り出してあげる。リスト型のままでは保存できないため、texts[0]でstr型に。
request.POST.getlist('text')の'text'はフォームで設定したname='text'。

解説2
誰がコメントしたのかも必要情報なので入れておかないとエラー吐く。

解説3
snippet_idなどとしたくなるが、int型の数値と捉えられてしまいForeignKeyフィールドでは認められない。Snippetモデルのpkがsnippet_idのやつと明示(Snippet(pk=snippet_id))してオブジェクト化が必須。
最後に、datasに格納して保存処理save()を実行。

5.リダイレクト処理

保存まで完了した後のレスポンスとしてdetail画面へのリダイレクト処理を行う。画面上ではコメントを入力し送信ボタンを押すと画面は遷移せず自分のコメントが追加されたような体になる。
注意点はリダイレクト先。snippet.detailだけでは飛ばないので第二引数としてsnippet.idを指定すること。

6.最終調整

ここで最初のHTMLに戻り、action先を入力。snippets/urls.pyで追加作成した保存用URLを指定。

[djangosnippets/snippets/templates/snippets/snippet_detail.html]

〜省略〜

<!-- formタグのction先を記載 -->
<form action="{% url 'comment' snippet.id %}" method="POST">
{% csrf_token %}
<h3>コメントを投稿する</h3>
<input type="text" name="text">
<input type="submit" value="コメント">
</form>

以上でコメント機能の実装が完了。あとは実際の画面で動作確認なり、css付けるなりしたらいいと思う。

この記事が気に入ったらサポートをしてみませんか?