FlaskでWTFormsでフォームを作った場合SelectFieldを動的に使いたいことがある。
データ
<option></option>
を動的に作る方法。
htmlで記述した場合
<select id="users">
<options value="1">日本 太郎</option>
<options value="2">山田 太郎</option>
<options value="3">山田 花子</option>
</select>
jinjaでの記述した場合
<form method="post>
{{ form.csrf_token }}
{{ form.name(id='users') }}
{{ form.submit }}
</form>
forms.py
class UserForm(Form):
name = SelectField('ユーザ名', coerce=int) # coerceにはstrで文字列でも値を格納できる。
submit = SubmitField('登録')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._set_users()
def _set_users(self):
users = Users.query.all()
# DBから取得したデータをchoiesにタプルで (valueの値, textの値) と格納するだけ。
self.users.choices = [(user.id, user.name) for user in users]
属性
動的に作られた<option></option>
に属性を追加する方法。
これが意外と苦戦したところ。
widgets.py
from wtforms.widgets.core import HTMLString, html_params, Markup, escape, text_type
class MySelect(object):
def __init__(self, multiple=False):
self.multiple = multiple
def __call__(self, field, **kwargs):
kwargs.setdefault('id', field.id)
if self.multiple:
kwargs['multiple'] = True
if 'required' not in kwargs and 'required' in getattr(field, 'flags', []):
kwargs['required'] = True
html = ['<select %s>' % html_params(name=field.name, **kwargs)]
# 選択できない初期値を追加する
html.append(self.render_option(value='', label='選択してください', selected=False, disabled=True))
for val, label, selected in field.iter_choices():
# 各<option></option>にstyleを適用させることが可能に。
html.append(self.render_option(val, label, selected, disabled=False, style='background-color:black'))
html.append('</select>')
return Markup(''.join(html))
@classmethod
def render_option(cls, value, label, selected, disabled, **kwargs):
if value is True:
# Handle the special case of a 'True' value.
value = text_type(value)
options = dict(kwargs, value=value)
if selected:
options['selected'] = True
# hiddenをここで追加。
if disabled:
options['hidden'] = True
return Markup('<option %s>%s</option>' % (html_params(**options), escape(label)))
forms.py
from widgets.py import MySelect
class Color(Form):
name = SelectField('ユーザ名', coerce=int, widget=MySelect()) # widgetを指定するだけ。
submit = SubmitField('登録')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._set_users()
def _set_users(self):
users = Users.query.all()
self.users.choices = [(user.id, user.name) for user in users]