ウィジェットは、Django が HTML の input 要素の表現方法です。ウィジェットは、HTML のレンダリングをコントロールして、ウィジェットに合致する GET/POST ディクショナリからデータを取り出します。
Built-in widgets によって作られる HTML は、<!DOCTYPE html> を対象とした HTML5 シンタックスを使います。たとえば、XHTML スタイルである checked='checked' よりも、checked といった boolean 属性を使います。
Tip
ウィジェットを フォームフィールド と混同しないでください。フォームフィールドは入力値の検証ロジックを扱い、テンプレート内で直接使用されます。ウィジェットはウェブページ上で input 要素から HTML の form をレンダリングし、submit された生データを取り出します。ウィジェットはフォームフィールドにアサイン (assigned) される必要があります。
フォーム上でフィールドを指定したときは、Django は描画されるデータのタイプに適したデフォルトのウィジェットを使用します。各フィールドで使われるウィジェットを調べるためには、 ビルトインの Field クラス を参照してください。
しかし、フィールドに別のウィジェットを使用したい場合は、フィールド定義で widget 引数を使用できます。例えば:
from django import forms
class CommentForm(forms.Form):
name = forms.CharField()
url = forms.URLField()
comment = forms.CharField(widget=forms.Textarea)
これは、comment のフォームに対して、デフォルトの TextInput ウィジェットではなく、より大きなサイズの Textarea ウィジェットを指定しています。
多くのウィジェットは、省略可能な追加の引数を持っています; これらは、フィールド上でウィジェットを定義する際にセットできます。以下の例では、SelectDateWidget に対して years 属性がセットされます:
from django import forms
BIRTH_YEAR_CHOICES = ["1980", "1981", "1982"]
FAVORITE_COLORS_CHOICES = {
"blue": "Blue",
"green": "Green",
"black": "Black",
}
class SimpleForm(forms.Form):
birth_year = forms.DateField(
widget=forms.SelectDateWidget(years=BIRTH_YEAR_CHOICES)
)
favorite_colors = forms.MultipleChoiceField(
required=False,
widget=forms.CheckboxSelectMultiple,
choices=FAVORITE_COLORS_CHOICES,
)
使用可能なウィジェットとそれぞれの引数の詳細については、ビルトインのウィジェット を参照してください。
Select ウィジェットを継承したウィジェット¶Select ウィジェットを継承したウィジェットは、選択肢を扱います。これらはユーザーに選択肢のリストを提示します。提示される選択肢はウィジェットによって異なります; Select ウィジェット自体は、<select> HTML のリスト表現を使い、一方で RadioSelect はラジオボタンを使います。
ChoiceField フィールドではデフォルトで Select ウィジェットが使用されます。ウィジェットに表示される選択肢は ChoiceField から継承され、ChoiceField.choices の変更は Select.choices を更新します。例えば:
>>> from django import forms
>>> CHOICES = {"1": "First", "2": "Second"}
>>> choice_field = forms.ChoiceField(widget=forms.RadioSelect, choices=CHOICES)
>>> choice_field.choices
[('1', 'First'), ('2', 'Second')]
>>> choice_field.widget.choices
[('1', 'First'), ('2', 'Second')]
>>> choice_field.widget.choices = []
>>> choice_field.choices = [("1", "First and only")]
>>> choice_field.widget.choices
[('1', 'First and only')]
とは言うものの、choices 属性を提供するウィジェットは、選択肢に基づかないフィールド (たとえば CharField) とともに使うことができますが、選択肢がモデルに継承され、単に表示されるだけのウィジェットではないときには、ChoiceField に基づいたフィールドを使うことをおすすめします。
Django がウィジェットを HTML としてレンダリングする際、非常に最小限のマークアップのみをレンダリングします。Django はクラス名やその他のウィジェット固有の属性を追加しません。したがって、例えばすべての TextInput ウィジェットはウェブページ上で同じように見えます。
ウィジェットをカスタマイズする方法には 2 つあります: ウィジェットごとのインスタンス と ウィジェットごとのクラス です。
1 つのウィジェットのインスタンスを他と異なる見た目にしたい場合、ウィジェットのオブジェクトをインスタンス化してフォームフィールドに割り当てるタイミングで、追加の属性を指定する必要があります (そしておそらく、あなたの CSS ファイルにいくつか記述を追加する必要もあります)。
たとえば、以下のフォームを取り上げます:
from django import forms
class CommentForm(forms.Form):
name = forms.CharField()
url = forms.URLField()
comment = forms.CharField()
このフォームには、名前とコメントフィールドに TextInput ウィジェットが、URL フィールドに URLInput ウィジェットが含まれます。それぞれにはデフォルトのレンダリングが適用され、CSS クラスや追加の属性はありません。
>>> f = CommentForm(auto_id=False)
>>> print(f)
<div>Name:<input type="text" name="name" required></div>
<div>Url:<input type="url" name="url" required></div>
<div>Comment:<input type="text" name="comment" required></div>
実際のウェブページでは、おそらくこれをカスタマイズしたいでしょう。コメント用には大きな入力要素を、名前用のウィジェットには特別な CSS クラスを指定したいかもしれません。また、HTML5 の異なる入力タイプを使用するために、 'type' 属性を指定することも可能です。これを行うには、ウィジェットを作成する際に Widget.attrs 引数を使用します。
class CommentForm(forms.Form):
name = forms.CharField(widget=forms.TextInput(attrs={"class": "special"}))
url = forms.URLField()
comment = forms.CharField(widget=forms.TextInput(attrs={"size": "40"}))
フォーム定義内でウィジェットを変更することもできます:
class CommentForm(forms.Form):
name = forms.CharField()
url = forms.URLField()
comment = forms.CharField()
name.widget.attrs.update({"class": "special"})
comment.widget.attrs.update(size="40")
あるいは、フォームに直接フィールドが宣言されていない場合(モデルフォームフィールドなど)、Form.fields 属性を使用できます:
class CommentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["name"].widget.attrs.update({"class": "special"})
self.fields["comment"].widget.attrs.update(size="40")
Django はこれで、レンダリングされたアウトプットに、追加的な要素を含むようになります:
>>> f = CommentForm(auto_id=False)
>>> print(f)
<div>Name:<input type="text" name="name" class="special" required></div>
<div>Url:<input type="url" name="url" required></div>
<div>Comment:<input type="text" name="comment" size="40" required></div>
attrs を使った HTML の id をセットすることもできます。BoundField.id_for_label で例を参照してください。
ウィジェットとともに、アセット (css や javascript) 、およびより詳細にカスタマイズされた見た目や動作を追加できます。
要するに、ウィジェットをサブクラス化して、 "Media" 内部クラスの定義 か "media" プロパティの作成 のどちらかをする必要があります。
これらのメソッドにはやや高度なPythonプログラミングが含まれており、トピックガイド フォームアセット で詳しく説明しています。
Widget や MultiWidget といった基本ウィジェットは、ビルトインのウィジェット によってサブクラス化され、カスタムウィジェットのための基礎となります。
ウィジェット¶この抽象クラスはレンダリングできませんが、基本的な属性 attrs を提供します。カスタムウィジェット上で render() を実行するかオーバーライドできます。
ディクショナリは、レンダリングされたウィジェット上にセットされる HTML の属性を含んでいます。
>>> from django import forms
>>> name = forms.TextInput(attrs={"size": 10, "title": "Your name"})
>>> name.render("name", "A name")
'<input title="Your name" type="text" name="name" value="A name" size="10">'
属性に True または False の値を割り当てると、HTML5 のブール属性としてレンダリングされます。
>>> name = forms.TextInput(attrs={"required": True})
>>> name.render("name", "A name")
'<input name="name" type="text" value="A name" required>'
>>>
>>> name = forms.TextInput(attrs={"required": False})
>>> name.render("name", "A name")
'<input name="name" type="text" value="A name">'
True をデフォルト値にする属性です。False にセットされた場合は、datetime と time のマイクロ秒の部分の値は、0 にセットされます。
ウィジェットのテンプレートで使うための値を整えて返します。value は入力値が検証されるとは限りません。そのため、サブクラスの実行は防衛的にプログラムされなければなりません。
ウィジェットテンプレートのレンダリングに使用する値の辞書を返します。デフォルトでは、辞書には単一のキー 'widget' が含まれています。これは、ウィジェットの辞書表現であり、以下のキーを含んでいます:
'name': name 引数からのフィールド名です。
'is_hidden': このウィジェットが非表示かどうかを示す真偽値です。
'required': このウィジェットのフィールドが必須かどうかを表す真偽値です。
'value': format_value() が返す値。
'attrs': レンダリングされるウィジェットに設定されるHTML属性。attrs 属性と attrs 引数の組み合わせです。
'template_name': self.template_name の値。
Widget のサブクラスは、このメソッドをオーバーライドすることで、カスタムのコンテキスト値を提供できます。
このウィジェットのHTML ID属性を返します。これは、フィールドのIDをもとにして <label> によって使用されます。IDが利用可能でない場合は、空の文字列を返します。
いくつかのウィジェットは複数の HTML 要素、そして、複数の ID を持っているため、このフックが必要です。この場合、このメソッドは、ウィジェットのタグの最初の ID と一致する ID 値を返す必要があります。
指定されたレンダラーを使用して、ウィジェットをHTMLにレンダリングします。renderer が None の場合、FORM_RENDERER の設定からレンダラーが使用されます。
データとこのウィジェットの名前のディクショナリが与えられ、このウィジェットの値を返します。files には request.FILES からのデータが含まれている可能性があります。値が指定されなかった場合は None を返します。 また、value_from_datadict はフォームデータの処理中に複数回呼び出される可能性があるので、カスタマイズして複雑な処理を追加する場合は、自分でキャッシュの仕組みを実装する必要があります。
data と files のディクショナリとこのウィジェットの名前が与えられ、ウィジェットのためのデータやファイルが存在するかを返します。
メソッドの結果は、モデルフォームのフィールドが デフォルトに逆戻りする かどうかに影響します。
特別なケースは CheckboxInput、CheckboxSelectMultiple、そして SelectMultiple で、これらは常に False を返します。なぜなら、チェックされていないチェックボックスや選択されていない <select multiple> はHTMLフォームの送信データに現れないため、ユーザーが値を送信したかどうか不明だからです。
レンダリング時にウィジェットが <fieldset> と <legend> でグループ化されるべきかを識別するための属性です。デフォルトは False ですが、 CheckboxSelectMultiple, RadioSelect, MultiWidget, SplitDateTimeWidget, SelectDateWidget のように複数の <input> タグを含むウィジェットの場合は True になります。
フォームフィールドの 初期の 値が与えられ、ウィジェットが 必要な HTML 属性とともにレンダリングできるかどうかを返します。フォームは、各フィールドに 必要な 属性をレンダリングするかどうかを決定するために、Field.required と Form.use_required_attribute とともにこのメソッドを使います。
デフォルトでは、隠されたウィジェットの場合は False を返し、それ以外の場合は True を返します。特別なケースは FileInput と ClearableFileInput で、これらは initial が設定されている場合に False を返し、 CheckboxSelectMultiple は常に False を返します。これは、ブラウザの検証がすべてのチェックボックスをチェックするのではなく、少なくとも1つをチェックすることを要求するためです。
ブラウザの検証と互換性のないカスタムウィジェットでは、このメソッドをオーバーライドしてください。たとえば、ある非表示の textarea 要素が背後にある WSYSIWG テキストエディタのウィジェットは、ブラウザが非表示のフィールドを検証するのを避けるため、常に False を返したがるかもしれません。
MultiWidget¶複数のウィジェットで構成されたウィジェットです。MultiWidget は MultiValueField と協調して動作します。
MultiWidget には 1 つの必要な引数があります。
必要なウィジェットを含むイテラブルです。例えば:
>>> from django.forms import MultiWidget, TextInput
>>> widget = MultiWidget(widgets=[TextInput, TextInput])
>>> widget.render("name", ["john", "paul"])
'<input type="text" name="name_0" value="john"><input type="text" name="name_1" value="paul">'
各サブウィジェットの name 属性にカスタムの接尾辞を指定するための辞書を指定します。この場合、各 (key, widget) のペアについて、キーがウィジェットの name に追加されて属性値を生成します。一つのウィジェットの接尾辞を抑制するには、単一のキーに空文字列 ('') を提供します。例えば:
>>> widget = MultiWidget(widgets={"": TextInput, "last": TextInput})
>>> widget.render("name", ["john", "paul"])
'<input type="text" name="name" value="john"><input type="text" name="name_last" value="paul">'
それと、1 つの必要なメソッドです:
このメソッドは、フィールドから単一の "圧縮された" 値を取り、"解凍された" 値のリストを返します。入力された値は検証済みと見なせますが、空 (empty)でないとは限りません。
このメソッドは、サブクラスによって 実行される必要があり、値が空 (empty) かもしれないので、処理は防衛的である必要があります。
"解凍" の裏にある根本的な原理は、フォームのフィールドから各ウィジェットの値に、結合された値を "分割する" 必要があることです。
以下は、SplitDateTimeWidget が どのように SplitDateTimeWidget の値を 2 つの分割された値に日付や時刻とともにリストに入れるかの例です:
from django.forms import MultiWidget
class SplitDateTimeWidget(MultiWidget):
# ...
def decompress(self, value):
if value:
return [value.date(), value.time()]
return [None, None]
Tip
MultiValueField が、正反対の責務 - 全メンバーフィールドの整えられた値を 1 つにまとめること - を持つ補完メソッド compress() を持っていることを覚えておいてください。
いくつかのカスタムコンテキストを提供します:
Widget.get_context() で説明されている 'widget' キーに加えて、 MultiWidget は widget['subwidgets'] キーを追加します。
これらはウィジェットテンプレートでループ処理が可能です:
{% for subwidget in widget.subwidgets %}
{% include subwidget.template_name with widget=subwidget %}
{% endfor %}
異なるセレクトボックス内で MultiWidget を日、月、年とともに日付を表示するためにサブクラス化するウィジェットの例です。このウィジェットは MultiValueField ではなく DateField とともに使われることを意図しています。そして、value_from_datadict() が実行されています:
from datetime import date
from django import forms
class DateSelectorWidget(forms.MultiWidget):
def __init__(self, attrs=None):
days = {day: day for day in range(1, 32)}
months = {month: month for month in range(1, 13)}
years = {year: year for year in [2018, 2019, 2020]}
widgets = [
forms.Select(attrs=attrs, choices=days),
forms.Select(attrs=attrs, choices=months),
forms.Select(attrs=attrs, choices=years),
]
super().__init__(widgets, attrs)
def decompress(self, value):
if isinstance(value, date):
return [value.day, value.month, value.year]
elif isinstance(value, str):
year, month, day = value.split("-")
return [day, month, year]
return [None, None, None]
def value_from_datadict(self, data, files, name):
day, month, year = super().value_from_datadict(data, files, name)
# DateField expects a single string that it can parse into a date.
return "{}-{}-{}".format(year, month, day)
コンストラクタはリスト内に複数の Select ウィジェットを作成します。super() メソッドはこのリストを使用してウィジェットを設定します。
必須のメソッド decompress() は、datetime.date 値をそれぞれのウィジェットに対応する日、月、年の値に分解します。存在しない日付、たとえば2月30日のような無効な日付が選択された場合、 DateField はこのメソッドに文字列を渡すため、それを解析する必要があります。最終的な return は、value が None の場合を処理します。これは、サブウィジェットに対してデフォルト値がないことを意味します。
デフォルトの value_from_datadict() の実装は、各 Widget に対応する値のリストを返します。これは、MultiValueField と一緒に MultiWidget を使用する場合に適しています。しかし、単一の値を取る DateField とこのウィジェットを使用したいため、このメソッドをオーバーライドしました。ここでの実装では、サブウィジェットからのデータを、DateField が期待する形式の文字列に組み合わせます。
Django は、全ての基本的な HTML ウィジェットの表現のほか、django.forms.widgets でいくつかの一般的に使われるウィジェットのグループを提供します。これは テキストの入力、多種のチェックボックスおよび選択肢、ファイルアップロード、複数値の入力の扱い を含みます。
これらのウィジェットは、 input と textarea の HTML 要素を使います。
TextInput¶NumberInput¶EmailInput¶URLInput¶PasswordInput¶DateInput¶input_type: 'text'
template_name: 'django/forms/widgets/date.html'
レンダリング結果: <input type="text" ...>
TextInput と同じ引数を取り、さらに 1 つ省略可能な引数があります:
このフィールドの初期値が描画されるフォーマットです。
format 引数が提供されない場合、デフォルトの形式は DATE_INPUT_FORMATS で最初に見つかった形式で、 表示形式のローカライズ を尊重します。 %U、%W、そして %j 形式は、このウィジェットではサポートされていません。
DateTimeInput¶input_type: 'text'
template_name: 'django/forms/widgets/datetime.html'
レンダリング結果: <input type="text" ...>
TextInput と同じ引数を取り、さらに 1 つ省略可能な引数があります:
このフィールドの初期値が描画されるフォーマットです。
format 引数が提供されていない場合、デフォルトのフォーマットは DATETIME_INPUT_FORMATS で見つかった最初のフォーマットであり、 表示形式のローカライズ を尊重します。 %U、%W、および %j フォーマットは、このウィジェットではサポートされていません。
デフォルトでは、時刻のマイクロ秒部分は常に 0 にセットされます。マイクロ秒が必要な場合は、サブクラスで supports_microseconds 属性を True にセットして使ってください。
TimeInput¶input_type: 'text'
template_name: 'django/forms/widgets/time.html'
レンダリング結果: <input type="text" ...>
TextInput と同じ引数を取り、さらに 1 つ省略可能な引数があります:
このフィールドの初期値が描画されるフォーマットです。
format 引数が渡されないとき、デフォルトのフォーマットは TIME_INPUT_FORMATS 内で最初に見つかったフォーマットで、表示形式のローカライズ を尊重します。
マイクロ秒の扱いについては、DateTimeInput を参照してください。
Textarea¶これらのウィジェットは HTML 要素の <select>, <input type="checkbox">, および <input type="radio"> を使用しています。
複数の選択肢をレンダリングするウィジェットは、各選択肢をレンダリングするために使用されるテンプレートを指定する option_template_name 属性を持っています。例えば、Select ウィジェットの場合、select_option.html は <select> のための <option> をレンダリングします。
CheckboxInput¶Select¶NullBooleanSelect¶SelectMultiple¶RadioSelect¶template_name: 'django/forms/widgets/radio.html'
option_template_name: 'django/forms/widgets/radio_option.html'
Select と似ていますが、<div> タグ内にラジオボタンのリストとしてレンダリングされます。
<div>
<div><input type="radio" name="..."></div>
...
</div>
生成されるマークアップをより詳細に制御するには、テンプレート内でラジオボタンをループ処理します。ウィジェットとして RadioSelect を使用するフィールド beatles を持つフォーム myform を例にとると:
<fieldset>
<legend>{{ myform.beatles.label }}</legend>
{% for radio in myform.beatles %}
<div class="myradio">
{{ radio }}
</div>
{% endfor %}
</fieldset>
これにより、以下のHTMLが生成されます:
<fieldset>
<legend>Radio buttons</legend>
<div class="myradio">
<label for="id_beatles_0"><input id="id_beatles_0" name="beatles" type="radio" value="john" required> John</label>
</div>
<div class="myradio">
<label for="id_beatles_1"><input id="id_beatles_1" name="beatles" type="radio" value="paul" required> Paul</label>
</div>
<div class="myradio">
<label for="id_beatles_2"><input id="id_beatles_2" name="beatles" type="radio" value="george" required> George</label>
</div>
<div class="myradio">
<label for="id_beatles_3"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" required> Ringo</label>
</div>
</fieldset>
これには <label> タグが含まれています。さらに詳しく制御したい場合は、各ラジオボタンの tag、choice_label、および id_for_label 属性を使用できます。例えば、このテンプレートは...
<fieldset>
<legend>{{ myform.beatles.label }}</legend>
{% for radio in myform.beatles %}
<label for="{{ radio.id_for_label }}">
{{ radio.choice_label }}
<span class="radio">{{ radio.tag }}</span>
</label>
{% endfor %}
</fieldset>
...次のHTMLを生成します:
<fieldset>
<legend>Radio buttons</legend>
<label for="id_beatles_0">
John
<span class="radio"><input id="id_beatles_0" name="beatles" type="radio" value="john" required></span>
</label>
<label for="id_beatles_1">
Paul
<span class="radio"><input id="id_beatles_1" name="beatles" type="radio" value="paul" required></span>
</label>
<label for="id_beatles_2">
George
<span class="radio"><input id="id_beatles_2" name="beatles" type="radio" value="george" required></span>
</label>
<label for="id_beatles_3">
Ringo
<span class="radio"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" required></span>
</label>
</fieldset>
ラジオボタンをループしないことにした場合 (例えば、テンプレートに {{ myform.beatles }} を含む場合)、上記のように、ラジオボタンは <div> タグ内に <div> タグで出力されます。
外側の <div> コンテナは、定義されている場合はウィジェットの id 属性を受け取り、定義されていない場合は BoundField.auto_id を受け取ります。
ラジオボタンをループ処理する際、label タグと input タグはそれぞれ for 属性と id 属性を含みます。各ラジオボタンには、要素のIDを出力するための id_for_label 属性があります。
CheckboxSelectMultiple¶template_name: 'django/forms/widgets/checkbox_select.html'
option_template_name: 'django/forms/widgets/checkbox_option.html'
SelectMultiple と似ていますが、チェックボックスのリストとしてレンダリングされます。
<div>
<div><input type="checkbox" name="..." ></div>
...
</div>
外側の <div> コンテナは、定義されている場合はウィジェットの id 属性を受け取り、定義されていない場合は BoundField.auto_id を受け取ります。
RadioSelect のように、ウィジェットの選択肢ごとに個々のチェックボックスをループ処理できます。しかし、RadioSelect とは異なり、フィールドが必須であっても、チェックボックスには required HTML属性が含まれません。なぜなら、ブラウザの検証ではすべてのチェックボックスをチェックすることが求められるのではなく、少なくとも1つをチェックすることが要求されるためです。
チェックボックスをループする際、label タグと input タグにはそれぞれ for 属性と id 属性が含まれます。各チェックボックスには、要素のIDを出力するための id_for_label 属性があります。
FileInput¶ClearableFileInput¶SplitDateTimeWidget¶template_name: 'django/forms/widgets/splitdatetime.html'
MultiWidget を使用して2つのウィジェットを組み合わせたラッパーです。日付用の DateInput と、時間用の TimeInput です。 DateTimeField ではなく、SplitDateTimeField と共に使用する必要があります。
SplitDateTimeWidget にはいくつかの任意の引数があります:
DateInput.format と同様。
TimeInput.format と同様
Widget.attrs に似ています。レンダリングされる DateInput および TimeInput ウィジェットに設定される HTML 属性を含む辞書です。これらの属性が設定されていない場合は、代わりに Widget.attrs が使用されます。
SelectDateWidget¶template_name: 'django/forms/widgets/select_date.html'
月、日、年ごとに1つずつ、計3つの Select ウィジェットをまとめたラッパーです。
いくつかのオプション引数を取ります:
"year" セレクトボックスで使用する年のオプションのリストまたはタプルです。デフォルトは、現在の年と次の9年が含まれたリストです。
"months" オプションで、セレクトボックスで使用する月の辞書です。
この辞書のキーは月の番号 (1から始まる) に対応し、値は表示される月です:
MONTHS = {
1: _("jan"),
2: _("feb"),
3: _("mar"),
4: _("apr"),
5: _("may"),
6: _("jun"),
7: _("jul"),
8: _("aug"),
9: _("sep"),
10: _("oct"),
11: _("nov"),
12: _("dec"),
}
DateField が必須でない場合、 SelectDateWidget はリストのトップに空の選択肢(デフォルトでは ---)を持ちます。このラベルのテキストは empty_label 属性で変更できます。 empty_label は string、list、または tuple であることができます。文字列が使用されると、すべてのセレクトボックスはこのラベルを持つ空の選択肢をそれぞれ持ちます。 empty_label が 3 つの文字列要素の list または tuple の場合、セレクトボックスはそれぞれ独自のカスタムラベルを持ちます。ラベルはこの順序である必要があります: ('year_label', 'month_label', 'day_label') 。
# A custom empty label with string
field1 = forms.DateField(widget=SelectDateWidget(empty_label="Nothing"))
# A custom empty label with tuple
field1 = forms.DateField(
widget=SelectDateWidget(
empty_label=("Choose Year", "Choose Month", "Choose Day"),
),
)
4月 02, 2025