DjangoのセレクトフォームのOptionタグにclass属性を追加する
ご無沙汰しました。公私ともちょっと忙しくしておりました。
今日の内容は、タイトルの通りなのですが、Djangoのフォーム・セレクトメニューのOptionタグにclass指定する方法についてです。
具体的には、こんなソースを出力したい訳です。
<form method="post" action="index"> <select name="menu"> <option value="banana" class="fruits">バナナ</option> <option value="apple" class="fruits">りんご</option> <option value="tomato" class="vegetables">とまと</option> <option value="carrot" class="vegetables">にんじん</option> </select> <input type="submit" value="send" /> </form>
何故そのようなことがしたいかと言えば、ここで指定したclassに応じて、jQueryで各optionタグの表示/非表示を切り替えたいからです。
さて、私のケースではデータベースに保存したレコードを元にメニューを作成したいので、forms.ModelChoiceFieldを使ってセレクトメニューを出力することにします。
このforms.ModelChoiceFieldのルートクラスは、forms.Fieldです。forms.ModelChoiceFieldに限らず、django.formsでの各種フィールドは、forms.Fieldのサブクラスです。forms.Fieldでコアな処理を行い、それと関連づけられたforms.widgets.WidgetのサブクラスでHTML出力を行うという構成らしい。
forms.ModelChoiceFieldクラスのHTML出力クラスを確認すると、forms.widgets.Selectクラスだと分かりました。
そこで、forms.widgets.Selectクラスのoptionタグ出力を行っているソースコードを確認します。
widgets.py
def render_option(self, selected_choices, option_value, option_label): option_value = force_text(option_value) if option_value in selected_choices: selected_html = mark_safe(' selected="selected"') if not self.allow_multiple_selected: # Only allow for a single selection. selected_choices.remove(option_value) else: selected_html = '' return format_html('<option value="{0}"{1}>{2}</option>', option_value, selected_html, force_text(option_label))
残念ながら、引数でclass指定とかは出来ない内容。
そこで、
- forms.ModelChoiceFieldのサブクラスを作成
- forms.widgets.Selectのサブクラスを作成
- #2のrender_option関数をオーバーライト。まずは元のrender_otpionのソースを丸々コピーして、class属性を出力する内容に書き換える
- #1のinit処理において、super.__init__を呼び出す。その引数のkwargsで#2で作成したクラスをwidget(=HTML出力クラス)として追加する。
のようなフローで実装することに決定。
myapp.forms
これをviews.pyから
form = MyForm()
とインスタンス生成すればOKです。