在WTForms中使用自定义属性创建选择域选项

Joh*_*ton 7 python flask wtforms

我正在尝试创建一个SelectFieldSelectMultipleField允许我为其<option>标签添加属性.我试图添加像data-id或其他的属性data-____.我无法弄清楚如何做到这一点,因为它似乎只能为<select>标签本身而不是选项添加属性.
最终结果应该是这样的:

<select id="regularstuff-here" name="regular-name-here">
  <option value="1" data-id="somedata here" >Some Name here</option>
  <option value="2" data-id="somedata here" >Some Name here</option>
</select>
Run Code Online (Sandbox Code Playgroud)

我假设我必须创建一个自定义小部件.如果我查看WTForms的源代码,我会看到该select小部件调用:

html.append(self.render_option(val, label, selected))
Run Code Online (Sandbox Code Playgroud)

如果我看一下这个方法:

@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))))
Run Code Online (Sandbox Code Playgroud)

因此,似乎您不能将任何额外的参数传递给呈现option标记的方法.

Joh*_*ton 6

我只是想说,无需猴子修补或重写 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>
Run Code Online (Sandbox Code Playgroud)

在此处查看会议:https :
//github.com/wtforms/wtforms/pull/81

  • 您实际上如何修改表单以集成这些额外选项? (3认同)

Mar*_*ark 6

如果您(像我一样)想将每个选项的自定义属性存储在选择数组上,而不是在渲染时提供,那么以下自定义的“AttribSelectField”和小部件应该会有所帮助。这些选择变成了 (value, label, render_args) 的 3 元组,而不是 (value, label) 的 2 元组。

from wtforms.fields  import SelectField
from wtforms.widgets import Select, html_params, HTMLString

class AttribSelect(Select):
    """
    Renders a select field that supports options including additional html params.

    The field must provide an `iter_choices()` method which the widget will
    call on rendering; this method must yield tuples of
    `(value, label, selected, html_attribs)`.
    """

    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, html_attribs in field.iter_choices():
            html.append(self.render_option(val, label, selected, **html_attribs))
        html.append('</select>')
        return HTMLString(''.join(html))

class AttribSelectField(SelectField):
    widget = AttribSelect()

    def iter_choices(self):
        for value, label, render_args in self.choices:
            yield (value, label, self.coerce(value) == self.data, render_args)

    def pre_validate(self, form):
         if self.choices:
             for v, _, _ in self.choices:
                 if self.data == v:
                     break
             else:
                 raise ValueError(self.gettext('Is Not a valid choice'))
Run Code Online (Sandbox Code Playgroud)

用法示例:

choices = [('', 'select a name', dict(disabled='disabled'))]
choices.append(('alex', 'Alex', dict()))
select_field = AttribSelectField('name', choices=choices, default='')
Run Code Online (Sandbox Code Playgroud)

它为第一个option标签输出以下内容:

<option disabled="disabled" selected ...
Run Code Online (Sandbox Code Playgroud)