今回のテーマは「簡単なトピック投稿画面の作成する」です。おまたせ致しました。ここまで時間が掛かりましたね。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-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.
何が考えられるでしょうか??
川原さん
以下コメントします。
> 前の「 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’と書き換えてみましたが、同様のエラーメッセージが出ること確認しました。