超初心者用: Djangoでウェブアプリを作る ④ユーザによるTopicの追加
ウェブアプリで重要な要素といえば、ユーザーがそれぞれのアカウントを持つことできるようにすることです。まずは、ユーザーが独自のトピックと entry を追加できるようなフォームを作ります。さらにDjango がどのようにハッカーからの攻撃に対処するのかを簡単に説明していきます。
ユーザーのデータを入力させる
ユーザーのアカウントを作るシステムを作る前に、データを入力させるページを作ります。新しいトピックや entry を加えたり、編集したり。 現在は superuser だけが admin site を通してのみこれらを行うことができます。一般のユーザーには admin site に入ってきてほしくないので、Djangoのデータ入力フォームを使用します。
トピックモデルフォーム
ユーザーに情報を入力させるページはフォームと呼ばれます。情報が入力されたとき、必ず情報の形式が正しいかどうかを確認しなければなりません。確認できたら、その情報をデータベースに保存します。ありがたいことに、Django はこれらをほとんど自動的に行ってくれます。
フォームを作る最もシンプルな方法は ModelForm を使用することです。まずは、forms.py を作ります。models.py と同じ階にこのファイルを作ってください。
# forms.py from django import forms from .models import Topic class TopicForm(forms.ModelForm): class Meta: model = Topic fields = ['text'] labels = {'text': ''}
まずは form module と Topic model をインポートします。TopicForm という名前のクラスと作り、forms.ModelForm クラスから継承します。
class Meta:
このクラスの中にある Metaクラスはどのモデルがフォームのベースになるのか、どのフィールドがフォームに含まれるのかをDjango に教えます。
model = Topic fields = ['text']
Topic モデルからフォームを作り、text フィールドだけを追加します。
labels = {'text': ''}
Django に text フィールドにラベルを作成しないように伝えています。
new_topic URL
新しいトピックを入力するためのURLパターンを作成します。URL はできる多で簡単なものがいいので、今回はhttp://localhost:8000/new_topic/を使用します。今までの手順通り、最初は learning_logs/urls.py に new_topic ページのURL パターンを追加します。
# learing_logs/url.py #--省略-- urlpatterns = [ #--省略-- # 新しいトピックを追加するページ url(r'^new_topic/$', views.new_topic, name='new_topic'), ]
このURLパターンは view function の new_topic() にリクエストします。
new_topic() function
new_topic() では2つの状態を対処しなければなりません。空欄とデータが入力されたときです。トピックを追加した後は、topics ページにリダイレクトするようにします。
# views.py from django.shortcuts import render #すでにある from django.http import HttpResponseRedirect from django.urls import reverse from .models import Topic #すでにある from .forms import TopicForm --省略-- def new_topic(request): """新しいトピックを追加""" if request.method != 'POST': # データが入力されてない、空欄 form = TopicForm() else: # POSTデータが送信された form = TopicForm(request.POST) if form.is_valid(): new_topic.save() return HttpResponseRedirect(reverse('practicing_logs:topics')) context = {'form': form} return render(request, 'practicing_logs/new_topic.html', context)
入力した後にリダイレクトするために、HttpResponseRedirect をインポートしました。reverse() はURLパターンからURLを決定します。また、TopicForm もインポートします。
GET と POST リクエスト
ウェブアプリを作るときには GETリクエストと POSTリクエストの2種類があります。GETリクエストはサーバーからデータを読むだけの時に使います。POSTリクエストはユーザーがデータを送信する必要があるときに使用します。今回はトピックを追加するので POSTリクエストを使用します。
new_topic() は request オブジェクトをパラメーターに取ります。ユーザーがこのページをリクエストした時に、ブラウザは GET リクエストを送ります。ユーザーがフォームに入力し送信した時に、ブラウザはPOSTリクエストを送信します。フォームが空欄だった場合は、GETリクエストとなります。
if request.method != 'POST':
リクエストが GET か POST のどちらであるかを決定します。POST ではない、つまりおそらくGETである場合は 空欄のフォームを返します。form という名前の TopicForm のインスタンスを作り、conext に入れて template に送ります。
form = TopicForm(request.POST)
リクエストが POST で else が実行された場合、TopicForm のインスタンスを作り、ユーザーが入力しrequest.POST に保存されたデータを送ります。
if form.is_valid():
送信されたデータが正しいフォーマットか確認が取れるまでデータベースに保存することはできません。is_valid() はすべての必須の項目が入力されているかどうかを確認します。(デフォルトですべての項目は必須となっています。)また、フォーマットが正しいかどうかも確かめます。例えば、models.py で text は200文字以内と定義しました。これが満たされているかをチェックします。これらの作業を自動的に行ってくれます。
new_topic.save() return HttpResponseRedirect(reverse('practicing_logs:topics'))
すべての確認が取れたら、save() が実行され、入力されたデータがデータベースに保存されます。そのあとは reverse() を使って topics ページのURLを取得し、HttpResponseRedirect() に送られます。topics ページでユーザーが入力したトピックが見えるはずです。
new_topic template
フォーム画面を表示させるための new_topic.html を作成します。
# new_topic.html {% extends "practicing_logs/base.html" %} {% block content %} <p>Add a new topic:</p> <form action="{% url 'practicing_logs:new_topic' %}" method='post'> {% csrf_token %} {{ form.as_p }} <button name="submit">add topic</button> </form> {% endblock content %}
またまた、base.html を使用しました。
<form action="{% url 'practicing_logs:new_topic' %}" method='post'>
HTMLフォームを定義します。action は入力されたデータをどこに送るのかをサーバーに伝えます。今回は、 view function の new_topic() です。送信の仕方は POST です。
Django は template tag の {% csrf_token %}を使ってサーバーへの侵入の攻撃を防ぎます。Django ではフォームを作成するのに、template variable {{ form.as_p }} を使用します。自動的にすべてのフィールドを作成してくれます。as_p はフォーム要素を段落形式で並ばせるように指示します。これはフォームをきれいに見せるシンプルな方法です。ただ、Django は送信ボタンは自動的に作らないので、こちらで定義する必要があります。
new_topic ページをリンクさせる
topics ページから new_topics ページに飛べるようにリンクを貼り付けます。
# topics.html {% extends "learning_logs/base.html" %} {% block content %} <p>Topics</p> <ul> --省略-- </ul> <a href="{% url 'practicing_logs:new_topic' %}">Add a new topic:</a> {% endblock content %}
今 topics ページを見ると new_topic ページへのリンクが追加されているはずです。new_topic ページは下のようになっているはずです。