超初心者用: 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 ページは下のようになっているはずです。

f:id:kendamaaa:20200427065133j:plain