1-14. 簡単なトピック投稿画面の作成する

今回のテーマは「簡単なトピック投稿画面の作成する」です。おまたせ致しました。ここまで時間が掛かりましたね。Django使っても全然Webアプリが出来上がらないじゃないかとお叱りを受けそうですが、何事も基礎が大事です。第一章でDjangoの使い方を大まかに捉えると応用は比較的簡単だと思います。

では掲示板の新規トピックを登録する画面を作っていきましょう。まずはフォームがあるだけのシンプルな登録画面を作ります。登録が完了するとTOP画面に作成したトピックが表示されるようにしましょう。

※本ページはDetailViewを使った詳細表示画面の作成まで読まれた方を対象としています。そのためサンプルソースコードが省略されている場合があります。


トピック登録画面のテンプレート作成

新しい画面を作成するのでまずはテンプレートを作りましょう。作成するファイルはtemplates/thread/create_topic.htmlです。

thread/create_topic.html

{% extends 'base/base.html' %}
{% block title %}トピック作成 - {{ block.super }}{% endblock %}
{% block content %}
<div class="ui grid stackable">
  <div class="eleven wide column">
    <div class="ui breadcrumb">
      <a href="{% url 'base:top' %}" class="section">TOP</a>
      <i class="right angle icon divider"></i>
      <a class="active section">トピック作成</a>
    </div>
    <div class="ui segment">
      <div class="content">
        <div class="header"><h3>トピック作成</h3></div>
        <form class="" action="{% url 'thread:create_topic' %}" method="POST">
          {% csrf_token %}
          {{form.as_p}}
          <button type="submit" class="ui button">作成</button>
        </form>
      </div>
    </div>
  </div>
  {% include 'base/sidebar.html' %}
</div>
{% endblock %}

キーポイントが二つあります。1つは{% csrf_token %}ですね。これはクロスサイトリクエストフォージェリという脆弱性に対するセキュリティ対策でトークンによって認証されないリクエストは受け付けない仕組みです。POSTメソッドでは必須です。(意図的csrf_tokenなしでアクセスすることもできますが、セキュリティ上の脆弱性を熟考の上判断して下さい。)

もう一つは{{form.as_p}}ですね。これはビュー側から’form’というコンテキストを受け取るために記述しているのですが、もう少し先で解説します。

フォームを使う

さて、ここでフォームというものが登場します。フォームというとHTMLの入力フォームを思い浮かべると思いますが、Djangoにおけるフォーム機能はもう少し幅の広い機能を提供します。ウェブアプリがフォームを持つ場合、以下のような処理が必要にあります。

  • データをレンダリングするための準備
  • HTMLとしてフォームをレンダリングすること
  • フォームから送信されたデータを処理すること

Djangoではフォームというパーツがこれらの処理を守備範囲として受け持ちます。詳細は公式ドキュメントも参照下さい。 フォームにはついては別の機会にもう少し詳しく扱う予定ですが、ここではとにかく使ってみましょう。今回はModelFormというフォームを使います。まず、thread/forms.pyを作成します。

thread/forms.py

from django.forms import ModelForm
from . models import Topic

class TopicCreateForm(ModelForm):
    class Meta:
        model=Topic
        fields=[
            'title',
            'user_name',
            'category',
            'message',
        ]

ModelFormを継承したTopicCreateFormを用意します。ModelFormを継承したクラスはMetaクラスにmodelとfieldsを指定することでフォームを作成出来ます。今回のようにモデルに対応するフォームを作成する場合は便利な方法です。(今回は扱いませんが、ModelFormを使わずフォームを生成する方法も当然あります。)

ビューを作成する

次にthread/views.pyに関数ベースのビューを追加していきます。

thread/views.py

# importは他の関数、クラス用も含む
from django.shortcuts import render, get_object_or_404, redirect
from django.views.generic import DetailView, TemplateView
from django.urls import reverse_lazy

from . models import Topic
from . forms import TopicCreateForm

#...省略

def simple_topic_create(request):
    """シンプルなトピック投稿用関数

    関数ベースの投稿機能作成例として実装

    """
    template_name = 'thread/create_topic.html'
    ctx = {}
    if request.method == 'GET':
        ctx['form'] = TopicCreateForm()
        return render(request, template_name, ctx)
    
    if request.method == 'POST':
        topic_form = TopicCreateForm(request.POST)
        if topic_form.is_valid():
            topic_form.save()
            return redirect(reverse_lazy('base:top'))
        else:
            ctx['form'] = topic_form
            return render(request, template_name, ctx)

GETメソッドでアクセスされた場合とPOSTメソッドでアクセスされた場合に処理を分けています。とくに明示しない場合はGETでのアクセスとして扱われます。TopicCreateFormのインスタンスをコンテキストで渡しています。これがthread/create_topic.htmlで描写されるというわけです。テンプレートの部分で説明を保留していましたが、先程フォームにはHTMLフォームとしてレンダリングする機能もあると説明しました。一番簡単なレンダリングは{{form}}とすることです。これは単順にHTMLのinputタグとlabelタグを並べるだけです。これをもう少し整形するための手法が以下の方法です

{{form.as_p}} : Pタグで囲んで段落毎に整形する。 {{form.as_ul}} : LIタグで囲んでリスト整形表示する。ただしULタグは別途記載する必要あり {{form.as_table}}:テーブル表示するための整形。ただしTABLEタグは別途記載する必要あり

今回はform.as_pを使って整形したということです。もうお気づきかも知れませんが、as_p, as_ul, as_tableはそれぞれ整形されたHTMLタグを返す関数です。

POSTメソッドで受けた場合にはis_valid()関数を呼んでデータの精査を行います。今回はModelFormを使用しているためTopicモデルが有している情報に適しているか精査されます。文字の長さやNullの許容等が正しくない場合は精査に失敗します。正しいデータが来た場合はフォームのsave()関数を呼んで保存しています。この方法はModelFormの場合のみ使える方法です。ややこしいので別の機会に触れます。データの精査に失敗した場合はtopic_formにエラーメッセージが入っていますので、これをコンテキストとして渡して再度create_topic.htmlを表示します。 では、追加した関数とURLを結びつけるためにthread/urls.pyに追加していきます。

thread/urls.py

from django.urls import path

from . import views
app_name = 'thread'

urlpatterns = [
    # トピック詳細画面
    path('/<int:pk>/', views.TopicDetailView.as_view(), name='topic'),
    #path('/<int:pk>/', views.TopicTemplateView.as_view(), name='topic'),
    #path('/<int:pk>/', views.detail_topic, name='topic'),

    # 今回追加。シンプルなトピック投稿画面
    path('/create_topic/', views.simple_topic_create, name='create_topic'),
]

これで準備は整いました。localhost:8080/thread/create_topic/にアクセスしてみましょう。

最後に

無事、トピックは登録出来ましたでしょうか?次回はDjangoにおけるフォームの役割についてもう少し見ていきたいと思います。

Sponsored Link


「1-14. 簡単なトピック投稿画面の作成する」への2件のフィードバック

  1. はじめまして。

    いつもこちらのサイト利用させていただいてます。
    ありがとうございます。

    この章でわからないことがありましたので
    コメントさせていただきました。

    お手数ですが、ご教示いただけると幸いです。

    前の「 1-13. DetailViewを使った詳細表示画面の作成」にて/thread/views.pyに
    class TopicTemplateView(TemplateView)とdef get_context_data(self, **kwargs)を
    定義していましたが、本章にて/thread/views.pyを書き換えているのですが、
    前の定義は不要となるのでしょうか??

    またフォームで入力後作成ボタンを押すと以下のメッセージが出ているのですが、
    何が悪いのかわからず解決できずにいます。

    NoReverseMatch at /
    Reverse for ‘topic’ not found. ‘topic’ is not a valid view function or pattern name.

    何が考えられるでしょうか??

  2. 川原さん

    以下コメントします。

    > 前の「 1-13. DetailViewを使った詳細表示画面の作成」にて/thread/views.pyに
    > class TopicTemplateView(TemplateView)とdef get_context_data(self, **kwargs)を
    > 定義していましたが、本章にて/thread/views.pyを書き換えているのですが、
    > 前の定義は不要となるのでしょうか??

    本文中に「thread/views.pyに関数ベースのビューを追加していきます。」と書いた通り書き換えではなくtopic_create関数を追加しています。TopicTemplateViewクラスは章が進むと不要となりますが、現時点では必要なクラスです。

    > またフォームで入力後作成ボタンを押すと以下のメッセージが出ているのですが、
    > 何が悪いのかわからず解決できずにいます。

    まずはしっかりエラーメッセージを読んだり調べたりすることをお勧めします。

    return redirect(reverse_lazy(‘base:top’))

    return redirect(reverse_lazy(‘base:topic’))
    と誤って入力していないでしょうか?

    エラーメッセージにある通りですが、関数やパターン名からパスに変換しようとしたが’topic’なる名前を見つけられなかったと言われています。サイトトップページにリダイレクトする際に’base:topic’を変換しようとしてエラーを吐いていると推察します。

    試しに手元の環境で’base:top’ -> ‘base:topic’と書き換えてみましたが、同様のエラーメッセージが出ること確認しました。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です