【问题标题】:Create selectfield options with custom attributes in WTForms在 WTForms 中使用自定义属性创建选择字段选项
【发布时间】:2014-06-21 01:39:39
【问题描述】:

我正在尝试创建一个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>

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

html.append(self.render_option(val, label, selected))

如果我看那个方法:

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

因此,您似乎无法将任何额外的参数传递给呈现option 标签的方法。

【问题讨论】:

  • 查看这篇文章 - stackoverflow.com/questions/23023788/… 我想你大概总结了一下。
  • @nsfyn55 感谢您确认这一点。非常感谢。
  • 因为您知道它现在是如何工作的,并且有几个人想要这样做。您甚至可以考虑向WTForms 项目提交补丁。
  • @nsfyn55 你可以在这里看到整个 convo:github.com/wtforms/wtforms/pull/81
  • 很有趣,非常感谢您的研究!

标签: python flask wtforms


【解决方案1】:

如果您(像我一样)想要将自定义属性存储在选项数组中,每个选项,而不是在渲染时提供,以下自定义的“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'))

使用示例:

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

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

<option disabled="disabled" selected ...

【讨论】:

  • 这是我一直在寻找的解决方案。谢谢老兄。
  • @jibinmathew 这是否有效?我在SelectField.pre_validate() 中得到too many values to unpack (expected 2)(当它在枚举选项时)。这是WTForms-2.1(这是旧的......)
  • 好点 - 我错过了 pre_validate 覆盖。我已经更新了答案。
  • @NealGokli 你有解决办法吗?
  • @jibinmathew 我没有,但请尝试上面的 Mark 编辑!我决定我不需要它来做我正在做的事情,但无论如何我可能很快就要用它来做其他事情了!
【解决方案2】:

我只是想说,无需猴子修补或重写 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

【讨论】:

  • 您如何实际修改表单以集成这些额外选项?
【解决方案3】:

作为 Mark 回答的替代方案,这里有一个自定义小部件(即“渲染器”字段),它允许在渲染时传递选项属性。

from markupsafe import Markup
from wtforms.widgets.core import html_params


class CustomSelect:
    """
    Renders a select field allowing custom attributes for options.
    Expects the field to be an iterable object of Option fields.
    The render function accepts a dictionary of option ids ("{field_id}-{option_index}")
    which contain a dictionary of attributes to be passed to the option.

    Example:
    form.customselect(option_attr={"customselect-0": {"disabled": ""} })
    """

    def __init__(self, multiple=False):
        self.multiple = multiple

    def __call__(self, field, option_attr=None, **kwargs):
        if option_attr is None:
            option_attr = {}
        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)]
        for option in field:
            attr = option_attr.get(option.id, {})
            html.append(option(**attr))
        html.append("</select>")
        return Markup("".join(html))

声明字段时,将CustomSelect 的实例作为widget 参数传递。

customselect = SelectField(
    "Custom Select",
    choices=[("option1", "Option 1"), ("option2", "Option 2")],
    widget=CustomSelect(),
)

在调用字段进行渲染时,传递一个选项 ID 字典(“{field_id}-{option_index}”),它定义了要传递给选项的属性字典。

form.customselect(option_attr={"customselect-0": {"data-id": "value"} })

【讨论】:

  • 这对我的用例非常有用(即使在使用 QuerySelectFiled 时),谢谢!稍微简化一下:def __call__(self, field, option_attr={}, **kwargs)
【解决方案4】:

我不确定我是否正确阅读了要求,但我有同样的要求 - 即添加到 SelectField 中的选项。就我而言,我只想添加一个选项,上面写着“选择一个选项...”,因为 SelectField 没有像 QuerySelectField 这样的空白条目的选项。这是使用 javascript onchange 触发器所必需的。但您可以添加 data.id、data.value 或其他任何内容。

我只是在烧瓶路线中这样做了:

# populate choices for Category drop down 
categories = Classification.query.filter_by(selectable=True).all()
all_cats = [cat.service for cat in categories]
unique_cat = list(dict.fromkeys(all_cats))  # remove duplicate names for Category drop down
unique_cat.sort()  #sort alphabetically
unique_cat.insert(0, 'Choose a category...')  # add this as first option in the drop down so onchange js is triggered
form.category.choices = unique_cat

最后两行与我们的要求最相关。 如果我查看生成的 HTML,它现在有额外的元素:

<select class="form-control" id="category" name="category">
  <option value="Choose a category...">Choose a category...</option>
  <option value="Accounts">Accounts</option>
  <option value="Business Applications">Business Applications</option>
</select>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-10
    • 1970-01-01
    • 2016-07-22
    • 2020-09-09
    • 2013-12-02
    • 2021-10-03
    • 2010-12-30
    • 1970-01-01
    相关资源
    最近更新 更多