revision-up-to: | 17812 (1.4) |
---|
このドキュメントについて
このドキュメントでは、 Django のフォーム処理機能を紹介しています。 フォーム API の特定のフィールドに関する詳細は、 フォーム API 、 フォームフィールド 、 フォームやフィールドのバリデーション を参照してくだ さい。
django.forms
は、 Django のフォーム処理ライブラリです。
フォームによって提出 (submit) されたデータの処理は、Django の
HttpRequest
クラスだけでも実現できます。しかし、フォー
ムライブラリを使うと、フォーム処理に必要な共通のタスクの面倒を見てくれます。
フォームライブラリを使えば、以下のようなことを実現できます:
このライブラリでは、以下のような概念を扱います:
<input type="text">
や <textarea>
のような、 HTML フォーム
ウィジェットに対応するクラスです。ウィジェットから HTML へのレンダ
リングもこのクラスで行われます。EmailField
はデー
タが有効な電子メールアドレスかどうか検証します。このライブラリは、データベースレイヤやビュー、テンプレートといった他の
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 つのフィールドをフォームに持たせます。
CharField
や EmailField
, 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 つのコードパスがあります:
ContactForm
イン
スタンスを生成して、テンプレートに渡します。request.POST
を使って束縛フォー
ムを生成します。入力データのバリデーションに成功したら、フォームデー
タを処理して、ユーザを「ありがとう」ページにリダイレクトします。束縛 フォームと 非束縛 フォームの違いはとても重要です。非束縛フォー ムには、何らデータが結び付いていません。非束縛フォームをレンダしてユーザに 呈示すると、フォームには空の値かデフォルトの値が表示されます。束縛フォーム にはユーザから提出されたデータが入るので、そのデータが有効であるかどうか調 べられます。入力データが無効な束縛フォームをレンダすると、入力データの何が おかしいのかを示すエラーメッセージが各行に出力されます。
束縛フォームと非束縛フォームの違いについてもっと詳しく知りたければ、 束縛フォームと非束縛フォーム を参照してください。
フォームからアップロードされたファイルを扱う方法は アップロードされたファイルをフォームに結びつける に詳しく書いてあります。
フォームの is_valid()
が True
を返すなら、入力データはフォームに設
定しておいたバリデーション条件を満たしているので、提出されたフォームを安全
に処理できます。この時点でも、 request.POST
には直接アクセスできますが、
form.cleaned_data
にアクセスする方がよいでしょう。
form.cleaned_data
内のデータはバリデーション済みであるだけでなく、適切
な Python の型に変換されているからです。上の例では、 cc_myself
はブール
型の値に変換されています。同様に、 IntegerField
や FloatField
は、
それぞれ 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>
タグに適した形式にラッピング
します。例 <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_fields
と visible_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 をカスタマイズ することを検討すべきです。
ここではフォームの基本を説明しましたが、フォームライブラリのできることはもっ とたくさんあります:
See also
Oct 26, 2017