If you look deep in the bowels of WTForms
you'll find a widget class called SelectField
this is the method called build the html string:
@classmethod
def render_option(cls, value, label, selected, **kwargs):
options = dict(kwargs, value=value)
if selected:
options['selected'] = True
return HTMLString('<option %s>%s</option>' % (html_params(**options), escape(text_type(label))))
this is __call__
method that invokes the render_options
function defined above.
def __call__(self, field, **kwargs):
kwargs.setdefault('id', field.id)
if self.multiple:
kwargs['multiple'] = True
html = ['<select %s>' % html_params(name=field.name, **kwargs)]
for val, label, selected in field.iter_choices():
html.append(self.render_option(val, label, selected))
html.append('</select>')
return HTMLString(''.join(html))
You are not going to be able to add the class
attribute by simply instantiating a SelectField
. When you do this it creates the Option
instances implicitly. At render time the render_options
methods of these implicit instances are only invoked with val
, selected
, and label
arguments.
You can access the implicit Option
instances after the fact. This is not without issue. If you look at @Johnston's example:
>>> i = 44
>>> form = F()
>>> for subchoice in form.a:
... print subchoice(**{'data-id': i})
... i += 1
He is doing exactly this. But you have to provide the attributes to the class at render time. The invocation subchoice(**{'data-id': i})
actually spits out the expected HTML
. This poses quite the problem if you are integrating WTForms
with a template engine. Because the something like jinja
is calling these render functions for you.
If you want this type of behavior I would recommend writing your own implementation of SelectField
that allows you to pass attributes into the implicit Option
instances. This way the template engine can just go about the business of invoking render
and you can keep your definitions of the form consolidated in your forms.py
file(s)