Wtfforms dynamic generation

陌路散爱 提交于 2019-12-12 05:25:10

问题


I have two wtfforms

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)

It works fine, but I want generate one of this form dynamically

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)

If I do so I get an exception on a form validation

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

I researched a several questions here with the tags about to wtfforms dynamic generation, but it didn't work for me. What I missed?


回答1:


the basic issue causing the error is that because you overrode __new__ and then called the super constructor, you consequentially bypassed Form.__init__ which passes a mapping of fields to BaseForm.__init__.

However Even trying to conform this interface probably won't end up getting you what you want, not without a significant amount more work. The reason is that WTForms uses a metaclass on Form which inspects fields and caches the unbound fields list at a point before your class is instantiated, and input processing is done at instantiation time, which requires all fields to have been declared by that point.


Following the tips from the Solving Specific Problems page, you can more safely create a dynamic form using one of these approaches:

1. Presuming SPORTS does not change after application initialization, we can simply create a top-level class and set attributes on it

class SportStartForm(Form):
    pass

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

2. alternately, if SPORTS is something that can change and is dynamic due to some user rules, it can be done in-view just like in the above linked page, or as a factory:

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

Usage of the factory model could then be something like:

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


来源:https://stackoverflow.com/questions/34373823/wtfforms-dynamic-generation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!