フォームを使う

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

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

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

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

HTML フォーム

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

いくつかのフォームインターフェイスの要素、例えばテキストインプットやチェックボックスは、とてもシンプルで HTML 自体に組み込まれています。そのほかに、とても複雑な要素もあります; Date ピッカーをポップアップさせたり、スライダーを動かしコントロールを操作するようなインターフェイスは、一般的に HTML フォームの <input> 要素だけではなく 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 はパスワードフォームには不適です。なぜならば、パスワードが URL 内はもちろんのこと、ブラウザーの履歴やサーバーログといったプレーンテキストの中すべてに表示されるからです。大量のデータや、画像のようなバイナリデータにも不適です。GET リクエストを admin フォームのために使ような Web アプリケーションは、セキュリティ上のリスクです: 攻撃者がシステムの繊細な部分へのアクセス権を得るために、フォームのリクエストを偽装することは簡単なのです。Django の CSRF 保護 のような他の保護と組み合わされた POST は、アクセスをより適切にコントロールできるようにします。

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

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

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

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

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

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

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

Django におけるフォーム

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

Web アプリケーションの文脈では、 'form' は HTML <form> 、それを提供する Django の Form 、データが送信されたときに返された体系化されたデータ、もしくは端末総合間で動くこれらパーツの集合を意味するかもしれません。

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 を返します
  • フォームのデータを :attr:`~Form.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.shortcuts import render
from django.http import HttpResponseRedirect

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 の種類とブラウザ検証

フォームが EmailField もしくは何か数値のフィールドタイプを含むとき、Django は urlemailnumber という HTML5 の input タイプを使います。デフォルトでは、ブラウザはこれらのフィールドにブラウザ自身の検証を適用します。これらは Django の検証より厳密かもしれません。このような動作を無効化したいときは、 form タグの novalidate 属性をセットするか、フィールドで TextInput のような異なるウィジェットを指定してください。

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

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

More about Django Form classes

All form classes are created as subclasses of either django.forms.Form or django.forms.ModelForm. You can think of ModelForm as a subclass of Form. Form and ModelForm actually inherit common functionality from a (private) BaseForm class, but this implementation detail is rarely important.

モデルとフォーム

In fact if your form is going to be used to directly add or edit a Django model, a ModelForm can save you a great deal of time, effort, and code, because it will build a form, along with the appropriate fields and their attributes, from a Model class.

Bound and unbound form instances

The distinction between Bound and unbound forms is important:

  • An unbound form has no data associated with it. When rendered to the user, it will be empty or will contain default values.
  • A bound form has submitted data, and hence can be used to tell if that data is valid. If an invalid bound form is rendered, it can include inline error messages telling the user what data to correct.

The form's is_bound attribute will tell you whether a form has data bound to it or not.

More on fields

Consider a more useful form than our minimal example above, which we could use to implement "contact me" functionality on a personal website:

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)

Our earlier form used a single field, your_name, a CharField. In this case, our form has four fields: subject, message, sender and cc_myself. CharField, EmailField and BooleanField are just three of the available field types; a full list can be found in Form fields.

ウィジェット

Each form field has a corresponding Widget class, which in turn corresponds to an HTML form widget such as <input type="text">.

In most cases, the field will have a sensible default widget. For example, by default, a CharField will have a TextInput widget, that produces an <input type="text"> in the HTML. If you needed <textarea> instead, you'd specify the appropriate widget when defining your form field, as we have done for the message field.

Field data

Whatever the data submitted with a form, once it has been successfully validated by calling is_valid() (and is_valid() has returned True), the validated form data will be in the form.cleaned_data dictionary. This data will have been nicely converted into Python types for you.

注釈

You can still access the unvalidated data directly from request.POST at this point, but the validated data is better.

In the contact form example above, cc_myself will be a boolean value. Likewise, fields such as IntegerField and FloatField convert values to a Python int and float respectively.

Here's how the form data could be processed in the view that handles this form:

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/')

ちなみに

For more on sending email from Django, see メールを送信する.

Some field types need some extra handling. For example, files that are uploaded using a form need to be handled differently (they can be retrieved from request.FILES, rather than request.POST). For details of how to handle file uploads with your form, see Binding uploaded files to a form.

Working with form templates

All you need to do to get your form into a template is to place the form instance into the template context. So if your form is called form in the context, {{ form }} will render its <label> and <input> elements appropriately.

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

Additional form template furniture

Don't forget that a form's output does not include the surrounding <form> tags, or the form's submit control. You will have to provide these yourself.

There are other output options though for the <label>/<input> pairs:

  • {{ form.as_table }} will render them as table cells wrapped in <tr> tags
  • {{ form.as_p }} will render them wrapped in <p> tags
  • {{ form.as_ul }} will render them wrapped in <li> tags

Note that you'll have to provide the surrounding <table> or <ul> elements yourself.

Here's the output of {{ form.as_p }} for our ContactForm instance:

<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>

Note that each form field has an ID attribute set to id_<field-name>, which is referenced by the accompanying label tag. This is important in ensuring that forms are accessible to assistive technology such as screen reader software. You can also customize the way in which labels and ids are generated.

See Outputting forms as HTML for more on this.

Rendering fields manually

We don't have to let Django unpack the form's fields; we can do it manually if we like (allowing us to reorder the fields, for example). Each field is available as an attribute of the form using {{ form.name_of_field }}, and in a Django template, will be rendered appropriately. For example:

{{ 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>

Complete <label> elements can also be generated using the label_tag(). For example:

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

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

Of course, the price of this flexibility is 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.

Looping over the form's fields

If you're using the same HTML for each of your form fields, you can reduce duplicate code by looping through each field in turn using a {% for %} loop:

{% 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 %}

Useful attributes on {{ field }} include:

{{ field.label }}
The label of the field, e.g. Email address.
{{ field.label_tag }}

The field's label wrapped in the appropriate HTML <label> tag. This includes the form's label_suffix. For example, the default label_suffix is a colon:

<label for="id_email">Email address:</label>
{{ field.id_for_label }}
The ID that will be used for this field (id_email in the example above). If you are constructing the label manually, you may want to use this in lieu of label_tag. It's also useful, for example, if you have some inline JavaScript and want to avoid hardcoding the field's ID.
{{ field.value }}
The value of the field. e.g someone@example.com.
{{ field.html_name }}
The name of the field that will be used in the input element's name field. This takes the form prefix into account, if it has been set.
{{ 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 simple string containing the error message.
{{ field.is_hidden }}
This attribute is True if the form field is a hidden field and False otherwise. It's not particularly useful as a template variable, but could be useful in conditional tests such as:
{% if field.is_hidden %}
   {# Do something special #}
{% endif %}
{{ field.field }}
The Field instance from the form class that this BoundField wraps. You can use it to access Field attributes, e.g. {{ char_field.field.max_length }}.

参考

For a complete list of attributes and methods, see BoundField.

Looping over hidden and visible fields

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 using the include tag to reuse it in other templates:

# In your form template:
{% include "form_snippet.html" %}

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

If the form object passed to a template has a different name within the context, you can alias it using the with argument of the include tag:

{% include "form_snippet.html" with form=comment_form %}

If you find yourself doing this often, you might consider creating a custom inclusion tag.

Back to Top