在WTForms SelectField中为选项添加CSS类

4 投票
2 回答
5102 浏览
提问于 2025-04-18 02:25

有没有人能告诉我怎么给choices的值添加CSS类呢?我想给每个选项的背景换成小图片,那我该怎么用wtforms和CSS来实现呢?

class RegisterForm(Form):
    username = TextField('username', [validators.Length(min=3, max=50), validators.Required()])
    img_url = SelectField('avatar', 
            choices=[('static/images/avatars/1.jpg', '1'), 
                ('static/images/avatars/2.jpg', '2'),
                ('static/images/avatars/3.jpg', '3'), 
                ('static/images/avatars/4.jpg', '4'),
                ('static/images/avatars/5.jpg', '5'), 
                ('static/images/avatars/6.jpg', '6'),
                ('static/images/avatars/7.jpg', '7'), 
                ('static/images/avatars/8.jpg', '8'),
                ('static/images/avatars/9.jpg', '9'), 
                ('static/images/avatars/10.jpg','10')])

2 个回答

4

我想告诉你,其实这个问题可以解决,不需要去修改库的代码或者重写wtforms。这个库本身是支持的,只是方式不太直接。我之所以知道这一点,是因为我遇到了和你一样的问题,试着为WTForms写了一个修复方案,还自己提交了一个PR,结果后来发现其实可以直接这样做(我花了好几天才弄明白这一点):

>>> from wtforms import SelectField, Form
>>> class F(Form):
...    a = SelectField(choices=[('a', 'Apple'), ('b', 'Banana')])
... 
>>> i = 44
>>> form = F()
>>> for subchoice in form.a:
...     print subchoice(**{'data-id': i})
...     i += 1
... 
<option data-id="44" value="a">Apple</option>
<option data-id="45" value="b">Banana</option>

你可以在这里看到讨论:
https://github.com/wtforms/wtforms/pull/81

6

如果你深入了解 WTForms 的内部,会发现一个叫 SelectField 的小组件类。

这是用来生成 HTML 字符串的方法:

@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))))

这是 __call__ 方法,它会调用上面定义的 render_options 函数。

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))

你不能仅仅通过创建一个 SelectField 来添加 class 属性。这样做会隐式地创建 Option 实例。在渲染时,这些隐式实例的 render_options 方法只会用 valselectedlabel 这些参数来调用。

你可以在之后访问这些隐式的 Option 实例,但这并不是没有问题。如果你看看 @Johnston 的例子:

>>> i = 44
>>> form = F()
>>> for subchoice in form.a:
...     print subchoice(**{'data-id': i})
...     i += 1

他正是这样做的。不过,你必须在渲染时提供类的属性。调用 subchoice(**{'data-id': i}) 实际上会输出预期的 HTML。如果你把 WTForms 和模板引擎结合使用,这就会造成问题。因为像 jinja 这样的模板引擎会为你调用这些渲染函数。

如果你想要这种行为,我建议你自己实现一个 SelectField,这样可以让你把属性传递给隐式的 Option 实例。这样模板引擎就可以直接调用 render,而你可以把表单的定义集中在你的 forms.py 文件中。

撰写回答