このドキュメントについて
このドキュメントでは、Web フォームの基本と、それらを Django で扱う方法を紹介しています。より詳しく知りたいときは、フォーム API の特定の領域、 フォーム API 、 Form fields または フォームとフィールドの検証 をご覧ください。
あなたの作ろうとしているウェブサイトやアプリケーションが、単にコンテンツを公開したり訪問者からのインプットを受け付けないサイトでない限り、フォームを理解し利用する必要があります。
Django はフォームの構築を助けるさまざまなツールやライブラリを提供しています。これらを利用することで、サイト訪問者からデータの入力を受け入れ、そのデータを処理したあと、入力に応じたレスポンスを返すことができるようになります。
HTMLでは、フォームは <form>...</form>
内の要素の集まりで、訪問者によるテキスト入力、オプション選択、オブジェクトやコントロールの操作などを可能にし、これらの情報をサーバーに送り返します。
いくつかのフォームインターフェイスの要素、例えばテキストインプットやチェックボックスは、とてもシンプルで HTML 自体に組み込まれています。そのほかに、とても複雑な要素もあります; Date ピッカーをポップアップさせたり、スライダーを動かしコントロールを操作するようなインターフェイスは、一般的に HTML フォームの 1
要素だけではなく JavaScriptと CSS を使って実現されます。
As well as its <input>
elements, a form must specify two things:
例として、Django admin にあるいくつかの <input>
要素のログインフォームを見てみましょう。ユーザー名のための type="text"
、パスワードのための type="password"
、そしてログインボタンのための type="submit"
があります。他にも Django が次の行動を決めるために使われる、ユーザーからは見えない隠しテキストフィールドもあります。
また、フォームのデータが <form>
の action
属性によって指定された URL - /admin/
- に送信されるべきこと、そして method
属性によって指定されたHTTP メカニズム - post
- を使って送信されるべきこともブラウザーに伝えています。
<input type="submit" value="Log in">
要素がトリガーされると、データは /admin/
に返されます。
GET
と POST
¶フォームを扱うときに使用する HTTP メソッドは、GET
と POST
だけです。
Django のログインフォームは、POST
メソッドを使って返されます。この中で、ブラウザがフォームデータをひとまとめにし、送信用にエンコードし、サーバーに送った上で、サーバーからの応答を受け取ります。
GET
は、対照的に、送信されたデータを文字列に添付し、URL を構成するために使います。URL はデータが送られるべきアドレスのほか、データキーと値を含みます。実際に、Django ドキュメンテーションで何かを検索してみると、フォーム https://docs.djangoproject.com/search/?q=forms&release=1
といった URL が生成されるのが分かるでしょう。
GET
と POST
は、一般的には異なる目的に使われます。
システムの状態を変更する可能性のあるあらゆるリクエスト - 例えばデータベース内での変更を行うリクエスト - では 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 admin を考えてみると、複数の異なるタイプのデータの、たくさんのアイテムがフォーム内に表示されるために準備を必要とし、HTML としてレンダリングされ、便利なインターフェイスを使って編集され、サーバーに返され、検証され、クリーンアップされます。そして、保存されたり次の処理のために値が渡されたりするのです。
Django のフォーム機能は、この仕事のうち膨大な部分を簡素化・自動化します。そしてさらに、多くのプログラマーたちが自分自身でコードを書くよりも安全にこれらを実現します。
Django は、フォームに含まれる仕事のうち、3つのまったく異なるパーツを扱います:
これら全ては手動でコードに書くことが できます が、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.
Form
クラス¶このコンポーネントシステムの中心は、Django の Form
クラスです。 Django モデルがオブジェクトの論理構造、その動作、そしてその部分が私たちに表現される方法を記述するのと同じように、 Form
クラスはフォームを記述し、それがどのように動作し表示されるかを決定します。
モデルクラスのフィールドがデータベースのフィールドに対応するのと同じように、フォームクラスのフィールドは HTML フォームの <input>
要素に対応します。 (ModelForm
はモデルクラスのフィールドと HTML フォームの <input>
要素を、Form
を通じて対応させます; これは Django amin が基づくものです)
フォームのフィールド自体はクラスです; フォームが送信されたとき、フォームデータを管理し、検証を実施します。 DateField
と FileField
は、非常に異なる種類のデータを扱い、異なることをしなければなりません。
フォームフィールドは、HTML "ウィジェット” - ユーザーインターフェイスの装置の一つです - としてブラウザ内のユーザーに表されます。それぞれのフィールドタイプは、適切なデフォルトの Widget class を持っていますが、これらは必要に応じてオーバーライドできます。
Django でオブジェクトをレンダリングするとき、通常は次のような手順を取ります。
テンプレートの中でフォームをレンダリングすることは、他のあらゆる種類のオブジェクトをレンダリングする働きとほとんど同じですが、いくつかの重要な違いがあります。
データを持たないモデルインスタンスのケースでは、テンプレート内で何かをするのが有益であることはほとんどありません。一方、未入力のフォームをレンダリングすることはまったく合理的です - これは、ユーザーに入力してほしいときに行うことです。
したがって、ビューの中でモデルインスタンスを扱うときには、一般的にデータベースから取り出します。フォームを処理するときには、一般的にビューの中でインスタンス化します。
フォームをインスタンス化するときは、空のままにするかあらかじめ入力しておくことができます。たとえば:
最後のケースが最も興味深いです。なぜならば、それはユーザーが単にウェブサイトを見るだけでなく、情報を送り返すことができるようにするからです。
ユーザーの名前を取得するために、ウェブサイトで簡単なフォームを作ることを考えてみましょう:
<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 にこれらを任せると、比較的簡単に実現させることができます。
Form
クラス¶HTML フォームをどのように見せたいかは、すでに分かっています。Django で実現するためのスタート地点は以下の通りです:
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に対して、ビューの中でインスタンス化する必要があります。
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 フォームを動かせるようになりました。
始めるために必要なことは以上ですが、フォームフレームワークはまだまだ多くのことを用意しています。上記で説明してきた基本が理解できたら、フォームシステムのほかの機能を理解し、もう少しだけ根本的な仕組みを身につける準備をするようお勧めします。
Form
クラスの詳細¶すべてのフォームクラスは、django.forms.Form
ないし django.forms.ModelForm
のサブクラスとして作成されています。ModelForm
も Form
のサブクラスとして考えられます。Form
や ModelForm
は実際には (プライベートの) BaseForm
クラスを継承していますが、 実装の詳細が重要になることはほとんどありません。
モデルとフォーム
実のところDjango モデルを直接追加または編集するためにフォームを使用する場合、ModelForm は Model
クラスから適切なフィールドと属性とともにフォームを作成するため、時間、労力、コードを大幅に節約できます。
Bound and unbound forms の区別は重要です:
フォームの is_bound
属性で、フォームがデータ bound かどうか調べることができます。
上記の最低限の例よりも実用的なフォームを考えましょう。個人のウェブサイトで "お問い合わせ" 機能を実装します:
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)
前回の例では、CharField
の your_name
という単一のフィールドを扱いました。今回は 4 つのフィールドがあります: subject
、message
、sender
、cc_myself
です。CharField
、EmailField
、BooleanField
はフィールドの種類です; すべての種類は Form fields で確認できます。
各フォームのフィールドは対応する Widget クラス を持ち、順に <input type="text">
のような HTML フォームウィジェットと対応します。
多くの場合、フィールドは最適なデフォルトウィジェットを持っています。例えば、CharField
は TextInput
ウィジェットを持ち、HTML 内で <input type="text">
を生成します。代わりに <textarea>
が必要な場合、message
フィールドで行っているように、フォームフィールドを定義する際に適切なウィジェットを指定してください。
フォームで送信されたデータが何であっても、is_valid()
を呼び出して正常に検証されると (そして is_valid()
が True
を返すと)、検証されたフォームデータは form.cleaned_data
ディクショナリに格納されます。 このデータは Python の型に適宜変換されています。
注釈
この時点では、request.POST
から検証前のデータを取り出せますが、検証済みのデータを使うのがよい方法です。
上の連絡先フォームの例では、cc_myself
はブール値になります。 同様に、IntegerField
や FloatField
などのフィールドは、それぞれ値を Python の int
および float
に変換します。
このフォームを処理するビューで、フォームデータを処理する方法は次のとおりです。
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_email
)。 ラベルを手動で作成する場合は、label_tag
の代わりにこのラベルを使用します。 たとえば、インラインJavaScriptがあり、フィールドのIDをハードコーディングしないようにしたい場合にも便利です。{{ field.value }}
someone@example.com
{{ field.html_name }}
{{ field.help_text }}
{{ field.errors }}
<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 }}
{% if field.is_hidden %}
{# Do something special #}
{% endif %}
{{ field.field }}
BoundField
がラップするフォームクラスの Field
。 Field
属性にアクセスするために使用することができます。 例: {{char_field.field.max_length}}
参考
属性とメソッドの完全な一覧については、 BoundField
を参照してください。
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'
...
Template rendering of forms was added.
ここまでで基本的なことをカバーしますが、フォームはもっと多くのことができます。
Media
クラス)
参考
2022年6月01日