フォームを使う

このドキュメントについて

このドキュメントでは、Web フォームの基本と、それらを Django で扱う方法を紹介しています。より詳しく知りたいときは、フォーム API の特定の領域、 フォーム APIForm fields または フォームとフィールドの検証 をご覧ください。

あなたの作ろうとしているウェブサイトやアプリケーションが、単にコンテンツを公開したり訪問者からのインプットを受け付けないサイトでない限り、フォームを理解し利用する必要があります。

Django はフォームの構築を助けるさまざまなツールやライブラリを提供しています。これらを利用することで、サイト訪問者からデータの入力を受け入れ、そのデータを処理したあと、入力に応じたレスポンスを返すことができるようになります。

HTML フォーム

HTMLでは、フォームは <form>...</form> 内の要素の集まりで、訪問者によるテキスト入力、オプション選択、オブジェクトやコントロールの操作などを可能にし、これらの情報をサーバーに送り返します。

いくつかのフォームインターフェイスの要素、例えばテキストインプットやチェックボックスは、とてもシンプルで HTML 自体に組み込まれています。そのほかに、とても複雑な要素もあります; Date ピッカーをポップアップさせたり、スライダーを動かしコントロールを操作するようなインターフェイスは、一般的に HTML フォームの 1 要素だけではなく JavaScriptと CSS を使って実現されます。

As well as its <input> elements, a form must specify two things:

  • どこで: ユーザーのインプットに対応するデータの宛先となるべきURL
  • どのように: データが返される方法として使われる HTTP メソッド

例として、Django admin にあるいくつかの <input> 要素のログインフォームを見てみましょう。ユーザー名のための type="text" 、パスワードのための type="password"、そしてログインボタンのための type="submit" があります。他にも Django が次の行動を決めるために使われる、ユーザーからは見えない隠しテキストフィールドもあります。

また、フォームのデータが <form>action 属性によって指定された URL - /admin/ - に送信されるべきこと、そして method 属性によって指定されたHTTP メカニズム - post - を使って送信されるべきこともブラウザーに伝えています。

<input type="submit" value="Log in"> 要素がトリガーされると、データは /admin/ に返されます。

GETPOST

フォームを扱うときに使用する HTTP メソッドは、GETPOST だけです。

Django のログインフォームは、POST メソッドを使って返されます。この中で、ブラウザがフォームデータをひとまとめにし、送信用にエンコードし、サーバーに送った上で、サーバーからの応答を受け取ります。

GET は、対照的に、送信されたデータを文字列に添付し、URL を構成するために使います。URL はデータが送られるべきアドレスのほか、データキーと値を含みます。実際に、Django ドキュメンテーションで何かを検索してみると、フォーム https://docs.djangoproject.com/search/?q=forms&release=1 といった URL が生成されるのが分かるでしょう。

GETPOST は、一般的には異なる目的に使われます。

システムの状態を変更する可能性のあるあらゆるリクエスト - 例えばデータベース内での変更を行うリクエスト - では POST を使うべきです。 GET は、システムの状態に影響を与えないリクエストのみに使われるべきです。

GET would also be unsuitable for a password form, because the password would appear in the URL, and thus, also in browser history and server logs, all in plain text. Neither would it be suitable for large quantities of data, or for binary data, such as an image. A web application that uses GET requests for admin forms is a security risk: it can be easy for an attacker to mimic a form's request to gain access to sensitive parts of the system. POST, coupled with other protections like Django's CSRF protection offers more control over access.

一方で、 GET は Web 検索フォームのようなものに適しています。なぜならば、 GET リクエストを表現している URL は、簡単にブックマーク、共有、再送信ができるからです。

フォームにおける Django の役割

フォームを扱うのは大変な仕事です。Django admin を考えてみると、複数の異なるタイプのデータの、たくさんのアイテムがフォーム内に表示されるために準備を必要とし、HTML としてレンダリングされ、便利なインターフェイスを使って編集され、サーバーに返され、検証され、クリーンアップされます。そして、保存されたり次の処理のために値が渡されたりするのです。

Django のフォーム機能は、この仕事のうち膨大な部分を簡素化・自動化します。そしてさらに、多くのプログラマーたちが自分自身でコードを書くよりも安全にこれらを実現します。

Django は、フォームに含まれる仕事のうち、3つのまったく異なるパーツを扱います:

  • データがレンダリングできるように準備し、再構成すること
  • データのために HTML フォームを生成すること
  • クライアントから送信されたフォームとデータを受け取り、処理すること

これら全ては手動でコードに書くことが できます が、Django がこれらすべてをうまく処理します。

Django におけるフォーム

HTML フォームについては簡単に説明しましたが、HTML <form> は必要となる仕掛けの一部分に過ぎません。

In the context of a web application, 'form' might refer to that HTML <form>, or to the Django Form that produces it, or to the structured data returned when it is submitted, or to the end-to-end working collection of these parts.

Django の Form クラス

このコンポーネントシステムの中心は、Django の Form クラスです。 Django モデルがオブジェクトの論理構造、その動作、そしてその部分が私たちに表現される方法を記述するのと同じように、 Form クラスはフォームを記述し、それがどのように動作し表示されるかを決定します。

モデルクラスのフィールドがデータベースのフィールドに対応するのと同じように、フォームクラスのフィールドは HTML フォームの <input> 要素に対応します。 (ModelForm はモデルクラスのフィールドと HTML フォームの <input> 要素を、Form を通じて対応させます; これは Django amin が基づくものです)

フォームのフィールド自体はクラスです; フォームが送信されたとき、フォームデータを管理し、検証を実施します。 DateFieldFileField は、非常に異なる種類のデータを扱い、異なることをしなければなりません。

フォームフィールドは、HTML "ウィジェット” - ユーザーインターフェイスの装置の一つです - としてブラウザ内のユーザーに表されます。それぞれのフィールドタイプは、適切なデフォルトの Widget class を持っていますが、これらは必要に応じてオーバーライドできます。

フォームをインスタンス化し、処理し、レンダリングする

Django でオブジェクトをレンダリングするとき、通常は次のような手順を取ります。

  1. ビューの中でオブジェクトを取得する (例えば、データベースからオブジェクトを取得する)
  2. オブジェクトをテンプレートのコンテクストに引き渡す
  3. テンプレートの変数を使って、オブジェクトを HTML マークアップに展開する

テンプレートの中でフォームをレンダリングすることは、他のあらゆる種類のオブジェクトをレンダリングする働きとほとんど同じですが、いくつかの重要な違いがあります。

データを持たないモデルインスタンスのケースでは、テンプレート内で何かをするのが有益であることはほとんどありません。一方、未入力のフォームをレンダリングすることはまったく合理的です - これは、ユーザーに入力してほしいときに行うことです。

したがって、ビューの中でモデルインスタンスを扱うときには、一般的にデータベースから取り出します。フォームを処理するときには、一般的にビューの中でインスタンス化します。

フォームをインスタンス化するときは、空のままにするかあらかじめ入力しておくことができます。たとえば:

  • 保存されたモデルインスタンスからのデータ (編集のための admin フォームの場合のように)
  • 他のソースから照合したデータ
  • 前回の HTML フォーム送信から受信したデータ

最後のケースが最も興味深いです。なぜならば、それはユーザーが単にウェブサイトを見るだけでなく、情報を送り返すことができるようにするからです。

フォームを作る

すべきこと

ユーザーの名前を取得するために、ウェブサイトで簡単なフォームを作ることを考えてみましょう:

<form action="/your-name/" method="post">
    <label for="your_name">Your name: </label>
    <input id="your_name" type="text" name="your_name" value="{{ current_name }}">
    <input type="submit" value="OK">
</form>

これは、POST メソッドを使って、URL /your-name/ にフォームデータを返すよう、ブラウザに通知します。テキストフィールド、"Your name:" ラベル、そして "OK" ボタンを表示します。テンプレートのコンテクストが current_name 変数を含む場合は、 your_name フィールドをあらかじめ入力するために使われます。

HTML フォームを含むテンプレートをレンダリングし、 current_name フィールドを適切に提供するビューが必要になります。

フォームが送信されるとき、サーバーに送信される POST リクエストは、フォームのデータを含みます。

今度は、その /your-name/ URL と対応するビューも必要です。このURLは、リクエストの中から適切なキー/値のペアを見つけ、それらを処理します。

これは非常にシンプルなフォームです。実践では、1つのフォームが数十または数百のフィールドを含んでいて、しかも多くがあらかじめ入力されている必要があるかもしれません。また、操作が完了する前にユーザーが何回も編集と送信を繰り返すことも考えられます。

フォームが送信される前でも、ブラウザ上で多少の検証が必要となるかもしれません; ユーザーがカレンダーから日付を選ぶといったような、より複雑なフィールドを使うこともあるでしょう。

Django にこれらを任せると、比較的簡単に実現させることができます。

Django でフォームを作る

Form クラス

HTML フォームをどのように見せたいかは、すでに分かっています。Django で実現するためのスタート地点は以下の通りです:

forms.py
from django import forms

class NameForm(forms.Form):
    your_name = forms.CharField(label='Your name', max_length=100)

これは、単一のフィールド (your_name) で Form クラスを定義します。人間が読みやすいラベルをフィールドに適用してあり、このラベルはレンダリングされたときに <label> の中に表示されます (このケースでは、仮にラベルの指定を省略したとしても、実際には指定した label が自動的に生成されるラベルと同じではありますが)。

フィールドの最大文字数は max_length によって定義されます。これは2つのことをします。HTML の <input> に maxlength="100"` を配置します (そしてブラウザはユーザーがこの数値以上の文字数をそもそも入力できないようにするはずです). これはさらに、Django がブラウザからフォームを受け取ったときにデータの文字数を検証することも意味します。

A Form instance has an is_valid() method, which runs validation routines for all its fields. When this method is called, if all fields contain valid data, it will:

  • True を返します
  • フォームのデータを cleaned_data 属性に配置します

フォーム全体は、初めてレンダリングされたとき、次のような見た目になります。

<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" maxlength="100" required>

<form> や送信ボタンを含んで いない ことに注意してください。私たち自身が、これらをテンプレート内で提供する必要があります。

ビュー

Django ウェブサイトに送り返されたフォームで他は、ビューによって処理されます。通常、ビューはフォームを発行したビューと同じものです。これは、いくつかの同じロジックを再利用することを可能にします。

フォームを処理するためには、フォームを発行したいと考えているURLに対して、ビューの中でインスタンス化する必要があります。

views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render

from .forms import NameForm

def get_name(request):
    # if this is a POST request we need to process the form data
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:
        form = NameForm(request.POST)
        # check whether it's valid:
        if form.is_valid():
            # process the data in form.cleaned_data as required
            # ...
            # redirect to a new URL:
            return HttpResponseRedirect('/thanks/')

    # if a GET (or any other method) we'll create a blank form
    else:
        form = NameForm()

    return render(request, 'name.html', {'form': form})

もし GET とともにビューにたどり着いた場合は、空のフォームインスタンスを生成し、レンダリングのためテンプレートの context に配置します。これは、初めて URL を訪れたときに起こると考えられることです。

POST リクエストを使ってフォームが送信された場合は、ビューはもう一度フォームインスタンスを生成し、リクエストからのデータを格納します: form = NameForm(request.POST) これは "フォームにデータをくくりつける" と呼ばれます (そして "くくりつけられた" フォームとなりました)。

フォームの is_valid() メソッドを呼び出します; もし True でない場合、フォームとともにテンプレートに戻ります。今回はフォームはもはや空にはならない (ただし くくりつけられていません) ため、HTML フォームには前回送信されたデータが入力され、必要に応じて編集や修正ができます。

is_valid()True の場合は、 cleaned_data 属性で検証済みのフォームデータを見出すことができます。このデータは、データベースを更新するために使ったり、次に進むための HTTP リダイレクトをブラウザに送る前に、何らかの処理をできます。

テンプレート

name.html テンプレートですべきことは多くありません。

<form action="/your-name/" method="post">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="Submit">
</form>

すべてのフォームのフィールドと属性は、Django のテンプレート言語によって、この {{ form }} から HTML マークアップに展開されます。

フォームとクロスサイトリクエストフォージェリ保護

Django は使いやすい クロスサイトリクエストフォージェリに対する保護 を提供しています。有効なCSRF保護とともに POST を通じてフォームを送信するときには、上記の例にあるように csrf_token テンプレートタグを使う必要があります。とは言うものの、テンプレートにおいてCSRF 保護はフォームと直接関係あるわけではないので、このドキュメントの以降の例では csrf_token は省略されています。

HTML5 の input の種類とブラウザ検証

If your form includes a URLField, an EmailField or any integer field type, Django will use the url, email and number HTML5 input types. By default, browsers may apply their own validation on these fields, which may be stricter than Django's validation. If you would like to disable this behavior, set the novalidate attribute on the form tag, or specify a different widget on the field, like TextInput.

これで、Django の Form によって記述され, ビューによって処理され、HTML <form> としてレンダリングされた Web フォームを動かせるようになりました。

始めるために必要なことは以上ですが、フォームフレームワークはまだまだ多くのことを用意しています。上記で説明してきた基本が理解できたら、フォームシステムのほかの機能を理解し、もう少しだけ根本的な仕組みを身につける準備をするようお勧めします。

Django の Form クラスの詳細

すべてのフォームクラスは、django.forms.Form ないし django.forms.ModelForm のサブクラスとして作成されています。ModelFormForm のサブクラスとして考えられます。FormModelForm は実際には (プライベートの) BaseForm クラスを継承していますが、 実装の詳細が重要になることはほとんどありません。

モデルとフォーム

実のところDjango モデルを直接追加または編集するためにフォームを使用する場合、ModelFormModel クラスから適切なフィールドと属性とともにフォームを作成するため、時間、労力、コードを大幅に節約できます。

くくりつけられた (bound) フォームインスタンス

Bound and unbound forms の区別は重要です:

  • unbound のフォームには連結したデータがありません。ユーザーにレンダリングするとき、空ないしデフォルト値となります。
  • bound のフォームは送信されたデータを持っており、したがってデータの有効性を検証できます。無効な bound フォームがレンダリングされている場合、ユーザーがデータを修正できるようにエラーメッセージを表示することができます。

フォームの is_bound 属性で、フォームがデータ bound かどうか調べることができます。

フィールドについて

上記の最低限の例よりも実用的なフォームを考えましょう。個人のウェブサイトで "お問い合わせ" 機能を実装します:

forms.py
from django import forms

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    message = forms.CharField(widget=forms.Textarea)
    sender = forms.EmailField()
    cc_myself = forms.BooleanField(required=False)

前回の例では、CharFieldyour_name という単一のフィールドを扱いました。今回は 4 つのフィールドがあります: subjectmessagesendercc_myself です。CharFieldEmailFieldBooleanField はフィールドの種類です; すべての種類は Form fields で確認できます。

ウィジェット

各フォームのフィールドは対応する Widget クラス を持ち、順に <input type="text"> のような HTML フォームウィジェットと対応します。

多くの場合、フィールドは最適なデフォルトウィジェットを持っています。例えば、CharFieldTextInput ウィジェットを持ち、HTML 内で <input type="text"> を生成します。代わりに <textarea> が必要な場合、message フィールドで行っているように、フォームフィールドを定義する際に適切なウィジェットを指定してください。

Field data

フォームで送信されたデータが何であっても、is_valid() を呼び出して正常に検証されると (そして is_valid()True を返すと)、検証されたフォームデータは form.cleaned_data ディクショナリに格納されます。 このデータは Python の型に適宜変換されています。

注釈

この時点では、request.POST から検証前のデータを取り出せますが、検証済みのデータを使うのがよい方法です。

上の連絡先フォームの例では、cc_myself はブール値になります。 同様に、IntegerFieldFloatField などのフィールドは、それぞれ値を Python の int および float に変換します。

このフォームを処理するビューで、フォームデータを処理する方法は次のとおりです。

views.py
from django.core.mail import send_mail

if form.is_valid():
    subject = form.cleaned_data['subject']
    message = form.cleaned_data['message']
    sender = form.cleaned_data['sender']
    cc_myself = form.cleaned_data['cc_myself']

    recipients = ['info@example.com']
    if cc_myself:
        recipients.append(sender)

    send_mail(subject, message, sender, recipients)
    return HttpResponseRedirect('/thanks/')

ちなみに

Django からメールを送信するのに詳しくは メールを送信する をご覧ください。

一部のフィールドタイプでは、余分な処理が必要になります。 たとえば、フォームを使用してアップロードされたファイルは、別々に処理する必要があります (request.POST ではなく、request.FILES から取得できます)。 フォームでファイルのアップロードを処理する方法の詳細については、Binding uploaded files to a form を参照してください。

フォームテンプレートを扱う

フォームをテンプレートにするために必要なことは、フォームインスタンスをテンプレートコンテキストに配置することだけです。 なのでフォームがコンテキストで form が呼ばれる場合、 {{form}} はその <label> 要素と <input> 要素を適切にレンダリングします。

フォームのレンダリングオプション

フォームテンプレート部品の追加

フォームの出力に、 <form> や送信ボタンを含んで いない ことに注意してください。これらはテンプレート内で提供する必要があります。

<label> / <input> の組には他の出力オプションもあります。

  • {{ form.as_table }}<tr> タグでラップしたテーブルのセルとしてレンダリングします。
  • {{ form.as_p }}<p> タグでラップしてレンダリングします。
  • {{ form.as_ul }}<li> タグでラップしてレンダリングします。

周囲の <table> 要素または <ul> 要素を自分で指定する必要があることに注意してください。

ContactForm インスタンスの {{form.as_p}} の出力は次のとおりです:

<p><label for="id_subject">Subject:</label>
    <input id="id_subject" type="text" name="subject" maxlength="100" required></p>
<p><label for="id_message">Message:</label>
    <textarea name="message" id="id_message" required></textarea></p>
<p><label for="id_sender">Sender:</label>
    <input type="email" name="sender" id="id_sender" required></p>
<p><label for="id_cc_myself">Cc myself:</label>
    <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>

各フォームフィールドには、id_<field-name> に設定されたID属性があり、付随するラベルタグによって参照されます。 これはフォームがスクリーンリーダーソフトウェアなどの支援技術に、アクセスできるようにする上で重要です。You can also customize the way in which labels and ids are generated.

詳しくは、 フォームをHTMLとしてアウトプットする を参照してください。

フィールドを手動でレンダリングする

Django にフォームのフィールドを展開させず、手作業で行うこともできます (例えばフィールドの並べ替えを許可するなど)。 各フィールドは、{{form.name_of_field}} を使用してフォームの属性として使用でき、Django テンプレートでは適切にレンダリングされます。例えば次のようにできます:

{{ form.non_field_errors }}
<div class="fieldWrapper">
    {{ form.subject.errors }}
    <label for="{{ form.subject.id_for_label }}">Email subject:</label>
    {{ form.subject }}
</div>
<div class="fieldWrapper">
    {{ form.message.errors }}
    <label for="{{ form.message.id_for_label }}">Your message:</label>
    {{ form.message }}
</div>
<div class="fieldWrapper">
    {{ form.sender.errors }}
    <label for="{{ form.sender.id_for_label }}">Your email address:</label>
    {{ form.sender }}
</div>
<div class="fieldWrapper">
    {{ form.cc_myself.errors }}
    <label for="{{ form.cc_myself.id_for_label }}">CC yourself?</label>
    {{ form.cc_myself }}
</div>

完全な <label> 要素は、 label_tag() を使用して生成することもできます。 例えば次のようにできます:

<div class="fieldWrapper">
    {{ form.subject.errors }}
    {{ form.subject.label_tag }}
    {{ form.subject }}
</div>

フォームのエラーメッセージをレンダリングする

The price of this flexibility is a bit more work. Until now we haven't had to worry about how to display form errors, because that's taken care of for us. In this example we have had to make sure we take care of any errors for each field and any errors for the form as a whole. Note {{ form.non_field_errors }} at the top of the form and the template lookup for errors on each field.

Using {{ form.name_of_field.errors }} displays a list of form errors, rendered as an unordered list. This might look like:

<ul class="errorlist">
    <li>Sender is required.</li>
</ul>

The list has a CSS class of errorlist to allow you to style its appearance. If you wish to further customize the display of errors you can do so by looping over them:

{% if form.subject.errors %}
    <ol>
    {% for error in form.subject.errors %}
        <li><strong>{{ error|escape }}</strong></li>
    {% endfor %}
    </ol>
{% endif %}

Non-field errors (and/or hidden field errors that are rendered at the top of the form when using helpers like form.as_p()) will be rendered with an additional class of nonfield to help distinguish them from field-specific errors. For example, {{ form.non_field_errors }} would look like:

<ul class="errorlist nonfield">
    <li>Generic validation error</li>
</ul>

See フォーム API for more on errors, styling, and working with form attributes in templates.

フォームのフィールドをループする

フォームフィールドごとに同じ HTML を使用している場合、 {% for %} ループを使用して各フィールドを順番にループすることで、重複するコードを減らすことができます。

{% for field in form %}
    <div class="fieldWrapper">
        {{ field.errors }}
        {{ field.label_tag }} {{ field }}
        {% if field.help_text %}
        <p class="help">{{ field.help_text|safe }}</p>
        {% endif %}
    </div>
{% endfor %}

{{ field }} の有用な属性は次のとおりです:

{{ field.label }}
フィールドのラベル。例) Email address.
{{ field.label_tag }}

フィールドのラベルは、適切なHTMLの <label> タグで囲まれています。 これにはフォームの label_suffix が含まれます。 たとえば、デフォルトの label_suffix は次のようになります。

<label for="id_email">Email address:</label>
{{ field.id_for_label }}
フィールドに使用されるID(上記の例では id_email)。 ラベルを手動で作成する場合は、label_tag の代わりにこのラベルを使用します。 たとえば、インラインJavaScriptがあり、フィールドのIDをハードコーディングしないようにしたい場合にも便利です。
{{ field.value }}
フィールドの値。例: someone@example.com
{{ field.html_name }}
input要素のnameフィールドで使用されるフィールドの名前。 フォームのプレフィックスが設定されていれば考慮に入れます。
{{ field.help_text }}
Any help text that has been associated with the field.
{{ field.errors }}
Outputs a <ul class="errorlist"> containing any validation errors corresponding to this field. You can customize the presentation of the errors with a {% for error in field.errors %} loop. In this case, each object in the loop is a string containing the error message.
{{ field.is_hidden }}
この属性は、フォームフィールドがhiddenフィールドの場合はTrue、それ以外の場合はFalseです。 テンプレート変数としては特に有用ではありませんが、以下のような条件テストに役立ちます。
{% if field.is_hidden %}
   {# Do something special #}
{% endif %}
{{ field.field }}
BoundField がラップするフォームクラスの FieldField 属性にアクセスするために使用することができます。 例: {{char_field.field.max_length}}

参考

属性とメソッドの完全な一覧については、 BoundField を参照してください。

Hidden および可視のフィールドをループする

If you're manually laying out a form in a template, as opposed to relying on Django's default form layout, you might want to treat <input type="hidden"> fields differently from non-hidden fields. For example, because hidden fields don't display anything, putting error messages "next to" the field could cause confusion for your users -- so errors for those fields should be handled differently.

Django provides two methods on a form that allow you to loop over the hidden and visible fields independently: hidden_fields() and visible_fields(). Here's a modification of an earlier example that uses these two methods:

{# Include the hidden fields #}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{# Include the visible fields #}
{% for field in form.visible_fields %}
    <div class="fieldWrapper">
        {{ field.errors }}
        {{ field.label_tag }} {{ field }}
    </div>
{% endfor %}

This example does not handle any errors in the hidden fields. Usually, an error in a hidden field is a sign of form tampering, since normal form interaction won't alter them. However, you could easily insert some error displays for those form errors, as well.

再利用可能なフォームテンプレート

If your site uses the same rendering logic for forms in multiple places, you can reduce duplication by saving the form's loop in a standalone template and overriding the forms template_name attribute to render the form using the custom template. The below example will result in {{ form }} being rendered as the output of the form_snippet.html template.

In your templates:

# In your template:
{{ form }}

# In form_snippet.html:
{% for field in form %}
    <div class="fieldWrapper">
        {{ field.errors }}
        {{ field.label_tag }} {{ field }}
    </div>
{% endfor %}

In your form:

class MyForm(forms.Form):
    template_name = 'form_snippet.html'
    ...
Changed in Django 4.0:

Template rendering of forms was added.