フォームの操作

revision-up-to:17812 (1.4)

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

このドキュメントでは、 Django のフォーム処理機能を紹介しています。 フォーム API の特定のフィールドに関する詳細は、 フォーム APIフォームフィールドフォームやフィールドのバリデーション を参照してくだ さい。

django.forms は、 Django のフォーム処理ライブラリです。

フォームによって提出 (submit) されたデータの処理は、Django の HttpRequest クラスだけでも実現できます。しかし、フォー ムライブラリを使うと、フォーム処理に必要な共通のタスクの面倒を見てくれます。 フォームライブラリを使えば、以下のようなことを実現できます:

  1. フォームウィジェットから、 HTML フォームを自動的に生成して表示できます。
  2. 提出されたデータに対して、バリデーション規則 (validation rule) を適用できま す。
  3. バリデーションエラーを検出したときに、フォームをエラーメッセージ付きで表示で きます。
  4. 提出されたデータを、適切な Python のデータ型に変換できます。

概要

このライブラリでは、以下のような概念を扱います:

ウィジェット (Widget)
<input type="text"><textarea> のような、 HTML フォーム ウィジェットに対応するクラスです。ウィジェットから HTML へのレンダ リングもこのクラスで行われます。
フィールド (Field)
データの検証を行うためのクラスです。例えば、 EmailField はデー タが有効な電子メールアドレスかどうか検証します。
フォーム (Form)
フィールドの集まりで、データの検証や HTML への表示方法が実装された ものです。
フォームメディア (Form Media)
フォームをレンダするときに必要な CSS や JavaScript リソースの定義です。

このライブラリは、データベースレイヤやビュー、テンプレートといった他の Django コンポーネントに対してカップリングしていません。このライブラリが依存 しているのは settings と django.utils の二つのヘルパ関数、そして国際化 のためのフックだけです (ただし、このライブラリを使うために国際化の機能を使 わねばならないわけではありません) 。

フォームオブジェクト

フォームオブジェクトは、フォームに含める一連のフィールドや、フォームに入力 した値を受理するために充足しなければならないバリデーション規則をカプセル化 します。フォームクラスは、 django.newforms.Form クラスをサブクラス化し、 Django のデータベースモデルによく似た方法でフォームのフィールドを定義して作 成します。

一例として、個人のウェブサイトでコンタクトフォームの機能を実装するときに使 うフォームを考えてみましょう:

from django import forms

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

フォームは Field オブジェクトの組み合わせでできています。今回の例では、 subject (題名)、 message (メッセージ)、 sender (送信者)、そして cc_myself (自分に CC する)、の 4 つのフィールドをフォームに持たせます。 CharFieldEmailField, BooleanField はフィールド型です。 フィールド型の一覧は フォームフィールド を参照してください。

ModelForm を使えば、フォームを使って Django のモデルを直接追加したり編集したりしたいときに、モデルと重複する記述 をせずにすみます。

ビュー内でフォームを使う

フォームをビュー内で処理するときの標準的なパターンを以下に示します:

def contact(request):
    if request.method == 'POST': # フォームが提出された
        form = ContactForm(request.POST) # POST データの束縛フォーム
        if form.is_valid(): # バリデーションを通った
            # form.cleaned_data を処理
            # ...
            return HttpResponseRedirect('/thanks/') # POST 後のリダイレクト
    else:
        form = ContactForm() # 非束縛フォーム

    return render_to_response('contact.html', {
        'form': form,
    })

このパターンには、 3 つのコードパスがあります:

  1. フォームデータが提出されていなければ、非束縛の ContactForm イン スタンスを生成して、テンプレートに渡します。
  2. フォームデータが提出されていれば、 request.POST を使って束縛フォー ムを生成します。入力データのバリデーションに成功したら、フォームデー タを処理して、ユーザを「ありがとう」ページにリダイレクトします。
  3. フォームデータが提出され、バリデーションに成功しなければ、束縛フォー ムインスタンスをテンプレートに渡します。

束縛 フォームと 非束縛 フォームの違いはとても重要です。非束縛フォー ムには、何らデータが結び付いていません。非束縛フォームをレンダしてユーザに 呈示すると、フォームには空の値かデフォルトの値が表示されます。束縛フォーム にはユーザから提出されたデータが入るので、そのデータが有効であるかどうか調 べられます。入力データが無効な束縛フォームをレンダすると、入力データの何が おかしいのかを示すエラーメッセージが各行に出力されます。

束縛フォームと非束縛フォームの違いについてもっと詳しく知りたければ、 束縛フォームと非束縛フォーム を参照してください。

フォームからアップロードされたファイルを扱う

フォームからアップロードされたファイルを扱う方法は アップロードされたファイルをフォームに結びつける に詳しく書いてあります。

フォームから入力されたデータを処理する

フォームの is_valid()True を返すなら、入力データはフォームに設 定しておいたバリデーション条件を満たしているので、提出されたフォームを安全 に処理できます。この時点でも、 request.POST には直接アクセスできますが、 form.cleaned_data にアクセスする方がよいでしょう。 form.cleaned_data 内のデータはバリデーション済みであるだけでなく、適切 な Python の型に変換されているからです。上の例では、 cc_myself はブール 型の値に変換されています。同様に、 IntegerFieldFloatField は、 それぞれ Python の整数型や浮動小数型の値に変換されています。 読み込み専用のフィールドは入力要素としてでなく、むしろテキストとして表示さ れ、かつそれらはサーバにポストで戻されることがないため、それらのフィールド は、 form.cleaned_data で利用できないということに注意してください ( また カスタマイズした celan() 関数で設定した値は何も影響を与えないでしょう) 。

上の例を拡張すると、フォームデータの処理は以下のように書けます:

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)

    from django.core.mail import send_mail
    send_mail(subject, message, sender, recipients)
    return HttpResponseRedirect('/thanks/') # Redirect after POST

この例ではメールを送信しています。Django からメールを送信する方法の詳細は メールの送信 を参照してください。

テンプレートを使ってフォームを表示する

フォームは Django のテンプレート言語を使うように設計されています。上の例で は、コンテキスト変数 form を使ってテンプレートに ContactForm インス タンスを渡しています。簡単なテンプレートの例を示します:

<form action="/contact/" method="POST">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>

フォームインスタンスはフィールドのマークアップだけを出力します。前後の <form> タグや、 submit ボタンは自分で追加します。

フォームとクロスサイトリクエストフォージェリ対策

Djangoは使いやすい クロスサイトリクエストフォージェリ対策 を搭載してます. 前述した例のように、 CSRF 保護機能を有 効にした POST を経由して送信するとき、テンプレートタグの csrf_token を使うべきです。しかしながら、 CSRF 保護はテンプレート内のフォームに直接紐づ いていないため、このタグは、このドキュメント内の以下の例では、省略されてい ます。

form.as_p を参照すると、各フィールドとラベルをパラグラフ (<p>) タグ で囲って出力します。上のテンプレートの例を出力すると、以下のようになります:

<form action="/contact/" method="post">
<p><label for="id_subject">Subject:</label>
    <input id="id_subject" type="text" name="subject" maxlength="100" /></p>
<p><label for="id_message">Message:</label>
    <input type="text" name="message" id="id_message" /></p>
<p><label for="id_sender">Sender:</label>
    <input type="text" name="sender" id="id_sender" /></p>
<p><label for="id_cc_myself">Cc myself:</label>
    <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>
<input type="submit" value="Submit" />
</form>

各フォームフィールドには、 id_<field-name> の形式で id 属性が付加されて おり、すぐそばのラベルで参照されています。これは、画面読み上げソフトウェア のような入出力補助技術でフォームを扱うために重要な仕組みです。 ラベルや idの出力方法はカスタマイズできます

form.as_table を使うと、各フィールドがテーブルの各行になるように出力で きます (自分で <table> タグで囲む必要があります)。また、 form.as_ul を使えば、リストとして出力できます。

フォームテンプレートのカスタマイズ

デフォルトの HTML 出力が気に入らなければ、 Django のテンプレート言語を使っ て、フォームの表示方法をいくらでもカスタマイズできます。例えば、前掲の例は 以下のように拡張できます:

<form action="/contact/" method="post">
    {{ form.non_field_errors }}
    <div class="fieldWrapper">
        {{ form.subject.errors }}
        <label for="id_subject">Email subject:</label>
        {{ form.subject }}
    </div>
    <div class="fieldWrapper">
        {{ form.message.errors }}
        <label for="id_message">Your message:</label>
        {{ form.message }}
    </div>
    <div class="fieldWrapper">
        {{ form.sender.errors }}
        <label for="id_sender">Your email address:</label>
        {{ form.sender }}
    </div>
    <div class="fieldWrapper">
        {{ form.cc_myself.errors }}
        <label for="id_cc_myself">CC yourself?</label>
        {{ form.cc_myself }}
    </div>
    <p><input type="submit" value="Send message" /></p>
</form>

各フォームフィールドは、その名前に従って、 {{ form.name_of_field }} で出力でき、フォームウィジェットを表示するための適切な HTML を生成します。 {{ form.name_of_field.errors }} はフォームエラーのリストを以下のような 無番号リストで表示します:

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

リストには errorlist という CSS があてられていて、表示スタイルを変更で きます。エラーの表示方法をもっと細かく制御したければ、ループを使って以下の ように表現できます:

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

フォームフィールドのループ

もしそれぞれのフォームフィルドで同じ HTML を利用するなら、順次それぞれのフィ ールドを繰り替えす {% for %} ループを利用することで、重複するコードを減ら すことができます:

<form action="/contact/" method="post">
    {% for field in form %}
        <div class="fieldWrapper">
            {{ field.errors }}
            {{ field.label_tag }}: {{ field }}
        </div>
    {% endfor %}
    <p><input type="submit" value="Send message" /></p>
</form>

このループの {{ field }} は、 BoundField のインスタンスです。 BoundField は以下の属性も持ち、テンプレート内で利用することができま す。

{{ field.label }}
フィールドのラベルです。例 Email address
{{ field.label_tag }}
フィールドの label をHTML の <label> タグに適した形式にラッピング します。例 <label for="id_email">Email address</label>
{{ field.value }}
フィールドの値です。例 someone@example.com
{{ field.help_text }}
フィールドに関連付けられている任意のヘルプテキストです。
{{ field.errors }}
このフィールドに一致する複数のバリデーションエラーを含んでいる <ul class="errorlist"> が表示されます。 {% for error in field.errors %} ループを利用してエラーの表示をカス タマイズすることができます。この場合、ループ内の各オブジェクトは、エラ ーメッセージを含んでいる単純な文字列です。
field.is_hidden

フォームフィールドが非表示フィールドの場合、この属性は True とな り、そうでなければ、 False になります。テンプレートの変数としては、 特に便利でありませんが、以下に示すような条件文のテストには役立つでし ょう。:

{% if field.is_hidden %}
   {# Do something special #}
{% endif %}

非表示と表示フィールドのループ

もしテンプレート内で、フォームを手動で表示しているなら、 つまり Django のデフ ォルトフォームレイアウトに反対するとき、非表示ではないフィールドより <input type="hidden"> フィールドは注意深くを処理するべきです。 例えば、非表示フィールドは何も表示しないので、エラーメッセージをそのフィールド の横に表示した場合、ユーザの混乱の原因になるでしょう。よって、それらの非表示フ ィールドのエラーに対しては、別の手段で対処するべきです。

Django は非表示、表示フィールドに対して、ループを可能にするフォームに、 hidden_fields()visible_fields() という独立した2つのメソッドを規定 しています。上記の例に対して、これら2つのメソッドを使った場合の、変更点は以下 のとおりである。:

<form action="/contact/" method="post">
    {# 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 %}
    <p><input type="submit" value="Send message" /></p>
</form>

この例では、非表示フィールドに対してのエラーは処理されません。たいていの場合、 非表示フィールドのエラーは、フォームの改ざんを表します。なぜなら、普通のフォー ムのインタラクションを、非表示フィールドに変更すべきではないからある。しかしな がら、フォームのエラーに対応するエラー表示を簡単に挿し込むこともできるでしょう。

hidden_fieldsvisible_fields メソッドは Django 1.1 で新しく 追加されました。

フォームテンプレートの再利用

複数の場所で同じ描画ロジックのフォームを利用するなら、独立したテンプレートにフォ ームのループと include タグを保存し、それを他のテンプレート内で再利用す ることで、重複を減らすことができます:

<form action="/contact/" method="post">
    {% include "form_snippet.html" %}
    <p><input type="submit" value="Send message" /></p>
</form>

# In form_snippet.html:

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

もしテンプレートに渡されたフォームオブジェクトが、コンテキスト内で異なった名前 を持っているなら、 include タグの引数に with を利用することでそれを 別名にすることができます:

<form action="/comments/add/" method="post">
    {% include "form_snippet.html" with form=comment_form %}
    <p><input type="submit" value="Submit comment" /></p>
</form>

もし、頻繁にこのようなことをするなら、 inclusion tag をカスタマイズ することを検討すべきです。