Wtforms动态生成

Wtfforms dynamic generation

我有两个wtfform

class SportStartForm(Form):
    ski = DateField(format='%d.%m.%Y')
    kitesurfing = DateField(format='%d.%m.%Y')
    windsurfing = DateField(format='%d.%m.%Y')
    surfing = DateField(format='%d.%m.%Y')


class UpdateUserForm(Form):
    sport_start_at = FormField(SportStartForm)

它工作正常,但我想动态生成一个这样的表单

class SportStartForm(Form):
    def __new__(cls, **kwargs):
       for s in SPORTS:
           setattr(cls, s, DateField(format='%d.%m.%Y'))
    return super(SportStartForm, cls).__new__(cls, **kwargs)

如果我这样做,我会在表单验证时遇到异常

for name, unbound_field in itertools.chain(fields, extra_fields):
TypeError: 'NoneType' object is not iterable

我在这里用关于 wtfforms 动态生成的标签研究了几个问题,但它对我不起作用。我错过了什么?

导致错误的基本问题是因为您覆盖了 __new__ 然后调用了超级构造函数,因此您绕过了 Form.__init__ which passes a mapping of fields to BaseForm.__init__

但是 即使尝试符合此接口,也可能不会最终得到您想要的东西,而不是没有大量的工作。原因是 WTForms 在 Form 上使用元 class 来检查字段并缓存未绑定字段列表 你的 class 被实例化之前,并且输入处理在实例化时完成,这要求所有字段都已在该点声明。


按照 Solving Specific Problems 页面中的提示,您可以使用以下方法之一更安全地创建动态表单:

1.假设SPORTS在应用程序初始化后不会改变,我们可以简单地创建一个top-levelclass并在其上设置属性

class SportStartForm(Form):
    pass

for s in SPORTS:
    setattr(SportStartForm, s, DateField(format='%d.%m.%Y'))

2. 或者,如果 SPORTS 是可以改变的,并且由于某些用户规则而动态变化,则可以像上面的链接那样完成 in-view页面,或作为工厂:

def factory(sports):
    # This form class is created in a local scope, so a new class object
    # is made each time your factory is called
    class SportStartForm(Form):
        pass

    for s in sports:
        setattr(SportStartForm, s, DateField(format='%d.%m.%Y'))

    return SportStartForm

工厂模型的用法可能类似于:

def view(request):
    form_class = factory(['tennis', 'golf', 'windsurfing'])
    form = form_class(request.form)
    # etc, rest of view