ウィジェットとは、Django で HTML の入力エレメントを表現するためのオブジェクトです。ウィジェットは、 HTML のレンダリングや、個々のウィジェットに対応するデータをGET/POST 辞書から抽出する処理を行います。
http://djangoproject.jp/doc/ja/1.0/ref/forms/widgets.html
Djangoのフォームは複数のフィールドをもっているわけだけど、そのFieldはウィジェットというのをもっている。これが実際にHTMLのレンダリングを行なっていて、他にもデータを受け渡したりもする、まぁ非常にHTML臭い、Formの末端って感じか。
例えばTextInputというウィジェットはinputタグをレンダリングしてくれて、CharFieldなどはこれを使っている。
ウィジェットというとフィールドのHTMLタグに属性を追加したいときに widgets=TextInput(attrs=...) とするくらいにしか思っていなかったけど、ウィジェットを自分で書けば結構色々できて面白い。
何ができるか
Django既存のウィジェットだけだと使える幅が狭い。自分で書けば色々できる。
これだと各項目(例えば「律ちゃん」)の後ろに何らかの要素を追加することができない。
出力されるHTMLは以下のようになって、まぁliタグで区切られてしまう。
<tr><th><label for="id_radio_0">嫁:</label></th><td><ul> <li><label for="id_radio_0"><input type="radio" id="id_radio_0" value="R" name="radio" /> 律ちゃん</label></li> <li><label for="id_radio_1"><input type="radio" id="id_radio_1" value="S" name="radio" /> サニキ</label></li> </ul></td></tr>
ちょこっと手を加えてやれば以下のようなことは簡単にできる。
書いてみたもの
VerboseRadioFieldRendererというものを書いてみた。
これは通常のラジオボタンの後ろに何でもいいからHTMLを渡せば表示してくれるというもの。
以下のように書けば良い。
VERBOSE_RADIO_CHOICES = ( (('R', u'律ちゃん'), u'<a href="http://www.tbs.co.jp/anime/k-on/">http://www.tbs.co.jp/anime/k-on/</a>'), (('S', u'サニキ'), u'<a href="http://asahi.co.jp/precure/">http://asahi.co.jp/precure/</a>'), ) class YomeForm(forms.Form): yome = forms.ChoiceField(choices=VERBOSE_RADIO_CHOICES, label=u'嫁', widget=forms.RadioSelect(renderer=VerboseRadioFieldRenderer))
そしたら
使い方解説
ChoiceFieldにRadioSelectを指定する際にRendererも渡している。RadioSelectはrendererという引数でレンダリング用のクラスを受け取ってくれるので、これに自前のRendererクラスを渡せばいい。便利。
ウィジェットはWidgetクラスを継承して作ってやればよくて、そのクラスのrenderメソッドがHTMLのレンダリングをする。ただ今回のRadioSelectの場合は受け取ったRendererクラスがrenderメソッドで使われているので、ウィジェットそのものは書かなくて良かった。
おわりに
今回はウィジェットのレンダリング(renderメソッド)の部分を記事にした。けどこれだけでは幅が狭い。
受け取った値を処理したり、Widgetにさらに別の値を受け取るようにして、それによって処理を変えるなどが考えられる。
次はそういったことを書こうかなーと思っている。例えばDjangoのClearableFileInputがすごく面白いけど、そういうこと。
ちなみにVerboseRadioFieldRenderer、この記事で紹介した使い方では非常に貧相。今回の例みたいな使い方だけじゃなくて、もっと良い使い方がある。別のウィジェット内で呼び出して使うと良くて、ラジオボタンの後ろに表示したいHTML(もちろん呼び出し元のウィジェットが生成、加工したもの)を渡すとなお良い。そういったことも次の記事で書こうかな。