ウィジェット

revision-up-to:17812 (1.4) unfinished

ウィジェットとは、Django で HTML の入力エレメントを表現するためのオブジェク トです。ウィジェットは、 HTML のレンダリングや、個々のウィジェットに対応す るデータをGET/POST 辞書から抽出する処理を行います。

ウィジェットの指定

フォームに何らかのフィールドを作成する場合、 Django は表示するデータの型に 応じて適切なデフォルトのウィジェットを使います。どのフィールドがどのウィ ジェットを使っているかは、 組み込みフィールドクラス のドキュメントを参照してください。

とはいえ、デフォルトとは違うウィジェットを使いたい場合もあるでしょう。その 場合には、以下の例のように、フィールドを定義する際に widget 引数を指定します:

from django import forms

class CommentForm(forms.Form):
    name = forms.CharField()
    url = forms.URLField()
    comment = forms.CharField(widget=forms.Textarea)

上のコードでは、 comment フィールドにデフォルトの TextInput ウィジェッ トではなく、より大きな Textarea ウィジェットを使うように指定しています。

ウィジェットに引数を設定する

多くのウィジェットにはオプションの追加引数があり、それらはフィールドの ウィジェットを定義する際に設定できます。次の例では、 years 属性が SelectDateWidget に設定されます:

from django.forms.fields import DateField, ChoiceField, MultipleChoiceField
from django.forms.widgets import RadioSelect, CheckboxSelectMultiple
from django.forms.extras.widgets import SelectDateWidget

BIRTH_YEAR_CHOICES = ('1980', '1981', '1982')
GENDER_CHOICES = (('m', 'Male'), ('f', 'Female'))
FAVORITE_COLORS_CHOICES = (('blue', 'Blue'),
                            ('green', 'Green'),
                            ('black', 'Black'))

class SimpleForm(forms.Form):
    birth_year = DateField(widget=SelectDateWidget(years=BIRTH_YEAR_CHOICES))
    gender = ChoiceField(widget=RadioSelect, choices=GENDER_CHOICES)
    favorite_colors = forms.MultipleChoiceField(required=False,
        widget=CheckboxSelectMultiple, choices=FAVORITE_COLORS_CHOICES)

どのようなウィジェットが利用可能で、それらがどの引数を受け付けるかについては、 組み込みウィジェット を参照してください。

Select ウィジェットを継承するウィジェット

Select ウィジェットを継承するウィジェットは選択肢を扱います。これらは 選ぶことのできるオプション一覧をユーザに提示します。ウィジェットによって オプション一覧の提示方法は異なります。 Select ウィジェットは HTML の <select> を、 RadioSelect はラジオボタンを使う、という具合です。

Select ウィジェットはデフォルトで ChoiceField フィールド によって使われます。このウィジェットは表示する選択肢を 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 ベースのフィールドに対して使うことを推奨します。

ウィジェットインスタンスのカスタマイズ

ウィジェットを HTML としてレンダリングする際、 Django は最小限の HTML しか 出力しません。すなわち、クラス定義やウィジェット固有の属性は一切付加しない のです。従って、例えばページ上にまったく同じ見栄えの TextInput ウィジェットが並ぶわけです。

ウィジェットごとに見栄えを変えたいのなら、各々のウィジェットに属性を指定し てやる必要があります。ウィジェットを指定するときに、レンダリング後の HTML に付加したい属性のリストを指定できます。

例えば、以下のような簡単なフォームを考えましょう:

from django import forms

class CommentForm(forms.Form):
    name = forms.CharField()
    url = forms.URLField()
    comment = forms.CharField()

このフォームは 3 つの TextInput ウィジェットからなり、デフォルトの レンダリングでは CSS クラスや属性は指定されていません。従って、各ウィジェットは 全く同じ入力ボックスとしてレンダリングされます:

>>> f = CommentForm(auto_id=False)
>>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" /></td></tr>
<tr><th>Url:</th><td><input type="text" name="url"/></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>

現実の Web ページでは、全ウィジェットが同じに見えるような見栄えを期待しない でしょう。コメント欄をもうちょっと大きな入力ボックスにしたかったり、 'name' ウィジェットに特別な CSS クラスを指定したかったりするかもしれま せん。そうするには、ウィジェット作成時に 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'}))

これで、 Django はレンダリング結果に追加の属性を組み込むようになります:

>>> f = CommentForm(auto_id=False)
>>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" class="special"/></td></tr>
<tr><th>Url:</th><td><input type="text" name="url"/></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" size="40"/></td></tr>

組み込みウィジェット

Django は、基本的な HTML ウィジェットすべてと、よく使われるウィジェットグルー プを提供しています:

Widget

class Widget

この抽象クラスはレンダリングできませんが、基本の属性 attrs を提供します。

attrs

レンダリングされるウィジェットに設定される HTML 属性を含んだ辞書です。

>>> name = forms.TextInput(attrs={'size': 10, 'title': 'Your name',})
>>> name.render('name', 'A name')
u'<input title="Your name" type="text" name="name" value="A name" size="10" />'

TextInput

class TextInput

テキスト入力: <input type='text' ...> です。

PasswordInput

class PasswordInput

パスワードテキスト入力: <input type='password' ...> です。

オプションの引数を一つ取ります。

render_value

検証エラー後にウィジェットが再表示される際、値が記入されるかどうかを 決定します (デフォルトは False です)。

render_value のデフォルト値は True から False に変更されました。

HiddenInput

class HiddenInput

hidden ウィジェット: <input type='hidden' ...> です。

MultipleHiddenInput

class MultipleHiddenInput

複数の hidden ウィジェット: <input type='hidden' ...> です。

複数の値を持つフィールド用に、複数の hidden ウィジェットを使うウィジェット です。

choices

この属性は、フィールドに choices 属性が無い場合には 指定する必要はありません。もし属性がある場合、 Fieldchoices 属性が更新されると、ここでの指定は上書きされます。

FileInput

class FileInput

ファイルアップロード: <input type='file' ...> です。

ClearableFileInput

class ClearableFileInput
リリースノートを参照してください

フィールドが入力必須ではなく、かつ初期データがある場合に、フィールドの値を クリアするためのチェックボックスを備えたファイルアップロード入力: <input type='file' ...> です。

DateInput

class DateInput

単純なテキストボックスで表現された、日付用の入力ウィジェット: <input type='text' ...> です。

オプションの引数を一つ取ります:

format

フィールド初期値の表示に使う書式。

format 引数が指定されない場合、デフォルトの書式は DATE_INPUT_FORMATS で最初に見つかった書式になり、かつ 書式ローカライズ が適用されます。

DateTimeInput

class DateTimeInput

単純なテキストボックスで表現された、日付/時刻用の入力ウィジェット: <input type='text' ...> です。

オプションの引数を一つ取ります:

format

フィールド初期値の表示に使う書式。

format 引数が指定されない場合、デフォルトの書式は DATETIME_INPUT_FORMATS で最初に見つかった書式になり、かつ 書式ローカライズ が適用されます。

TimeInput

class TimeInput

単純なテキストボックスで表現された、時刻用の入力ウィジェット: <input type='text' ...> です。

オプションの引数を一つ取ります:

format

フィールド初期値の表示に使う書式。

format 引数が指定されない場合、デフォルトの書式は TIME_INPUT_FORMATS で最初に見つかった書式になり、かつ 書式ローカライズ が適用されます。

Textarea

class Textarea

テキストエリア: <textarea>...</textarea> です。

CheckboxInput

class CheckboxInput

チェックボックス: <input type='checkbox' ...> です。

オプションの引数を一つ取ります:

check_test

CheckboxInput の値を受け取り、チェックボックスにチェックを入れるべき ならば True を返す呼び出し可能オブジェクト。

Select

class Select

セレクタ: <select><option ...>...</select> です。

choices

この属性は、フィールドに choices 属性が無い場合には 指定する必要はありません。もし属性がある場合、 Fieldchoices 属性が更新されると、ここでの指定は上書きされます。

NullBooleanSelect

class NullBooleanSelect

「不明」, 「はい」, 「いいえ」 を選択肢とするセレクタです。

SelectMultiple

class SelectMultiple

Select と似ていますが、複数選択が可能なセレクタ: <select multiple='multiple'>...</select> です。

RadioSelect

class RadioSelect

Select と似ていますが、 <li> タグを使ったラジオボタンのリスト としてレンダリングされます:

<ul>
  <li><input type='radio' ...></li>
  ...
</ul>
リリースノートを参照してください

生成されるマークアップをもっと細かくコントロールしたければ、テンプレート内で ラジオボタンに対してループを回すことができます。仮に myform という フォームがあり、そこに RadioSelect をウィジェットに指定した beatles というフィールドがあると仮定すると:

{% for radio in myform.beatles %}
<div class="myradio">
    {{ radio }}
</div>
{% endfor %}

これは次の HTML を生成します:

<div class="myradio">
    <label><input type="radio" name="beatles" value="john" /> John</label>
</div>
<div class="myradio">
    <label><input type="radio" name="beatles" value="paul" /> Paul</label>
</div>
<div class="myradio">
    <label><input type="radio" name="beatles" value="george" /> George</label>
</div>
<div class="myradio">
    <label><input type="radio" name="beatles" value="ringo" /> Ringo</label>
</div>

<label> タグに囲まれていますね。さらに細かくコントロールしたければ、 ラジオボタンごとの tagchoice_label 属性を使えます。例えば このテンプレート...

{% for radio in myform.beatles %}
    <label>
        {{ radio.choice_label }}
        <span class="radio">{{ radio.tag }}</span>
    </label>
{% endfor %}

...は次の HTML になります:

<label>
    John
    <span class="radio"><input type="radio" name="beatles" value="john" /></span>
</label>
<label>
    Paul
    <span class="radio"><input type="radio" name="beatles" value="paul" /></span>
</label>
<label>
    George
    <span class="radio"><input type="radio" name="beatles" value="george" /></span>
</label>
<label>
    Ringo
    <span class="radio"><input type="radio" name="beatles" value="ringo" /></span>
</label>

もしラジオボタンに対してループを回さない – つまり単純にテンプレートで {{ myform.beatles }} と書く – と決めたなら、上に記した通り、 それらは <li> タグを使った <ul> の中に出力されることになるでしょう。

CheckboxSelectMultiple

class CheckboxSelectMultiple

SelectMultiple と似ていますが、チェックボックスのリストとして レンダリングされます:

<ul>
  <li><input type='checkbox' ...></li>
  ...
</ul>

MultiWidget

class MultiWidget

複数のウィジェットをラップするウィジェットです。おそらく MultiValueField と共に使いたくなるでしょう。

これの render() メソッドは、どうやって一つの値を複数のウィジェットで 表示するかを決める必要があるため、他のウィジェットのものとは異なります。

これのサブクラスは format_output メソッドを独自に実装しているかも しれません。 format_output メソッドは、レンダリングされたウィジェットの リストを受け取って、それらを自由に書式化し、 HTML の文字列を返します (訳注: format_output メソッドはウィジェットのレンダリング結果をカスタマイズする フックです)。

レンダリング時に使われる引数 value は次の 2 つのいずれかになります:

  • 一つの list
  • 値の list を「圧縮」表現した一つの値 (例えば文字列) 。

2 つ目の場合 – すなわちリスト ではない 場合 – render() はまず レンダリングする前に値を list に展開します。これは、 MultiWidget のサブクラスが実装しなければならない decompress() メソッドを呼ぶことで行われます。このメソッドは一つの「圧縮」された値を 受け取り、list を返します。一例として、 SplitDateTimeWidget がどうやって datetime の値を 日付と時刻という 2 つの値に分離したリストに変換しているかを挙げましょう:

class SplitDateTimeWidget(MultiWidget):

    # ...

    def decompress(self, value):
        if value:
            return [value.date(), value.time().replace(microsecond=0)]
        return [None, None]

render() が HTML レンダリングを実行するとき、リスト中の各値は対応する ウィジェットを使ってレンダリングされます – 最初の値は最初のウィジェットで、 2 つ目の値は 2 つ目のウィジェットで、という具合です。

MultiWidget は 1 つ指定必須の引数があります:

widgets

必要なウィジェットを含んだイテレーション可能オブジェクト。

SplitDateTimeWidget

class SplitDateTimeWidget

日付用の DateInput と、時刻用の TimeInput を、 MultiWidget を使ってラップしたものです。

SplitDateTimeWidget には 2 つオプションの属性があります:

date_format

DateInput.format と似ています。

time_format

TimeInput.format と似ています。

SplitHiddenDateTimeWidget

class SplitHiddenDateTimeWidget

SplitDateTimeWidget と似ていますが、 HiddenInput を日付と時間の両方に使用します。

SelectDateWidget

class SelectDateWidget

年・月・日それぞれ用の Select ウィジェット 3 つをラッピングしたウィジェットです。なおこのウィジェットは標準の ウィジェットとは異なるファイルで提供されていますので注意してください。

オプションの引数を一つ取ります。

years

オプションの、”year” セレクタで使用する年のリストまたはタプルです。 デフォルトでは現在の年から 9 年先までを含んだリストになります。