问题
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