WTForms: I can't seem to dynamically give a Qu

2020-07-27 04:51发布

问题:

I have a form which looks sort of like this:

class AddProductForm(Form):
    title = TextField('Title')
    type = QuerySelectField('Type',
        query_factory=lambda: ProductType.query.order_by(ProductType.sequence).all())

    def __init__(self, formdata=None, obj=None, prefix='', **kwargs):
        try:
            product_type_id = ProductType.query.filter_by(name=obj['product_type']).one().product_type_id
            kwargs.setdefault('type', product_type_id)
        except NoResultFound:
            pass

        Form.__init__(self, formdata, obj, prefix, **kwargs)

As you can see I am trying to set this up to give a sensible default for the product_type when the form is loaded. However, while this kind of code worked for setting a title as an example, it isn't working for the QuerySelectField 'type'. Does anybody have any ideas how I could fix this?

Supposing that this isn't possible, does anybody know how I could dynamically add form elements to a form?

回答1:

To define a default value in a QuerySelectField of WTForms, you can do it directly in the field definition:

my_field = QuerySelectField(
    label=u"My Label",
    query_factory=lambda: Model.query(...),
    get_pk=lambda item: item.id,
    get_label=lambda item: item.name,
    allow_blank=True,
    default=lambda: Model.query.filter(...).one())

However, it is a little less simple than what it looks. Take this into account:

  • The default value must be a callable
  • This callable must return an object instance of your model (not its pk, but the whole object)
  • It will only be used if no obj or formdata is used to initialize the form instance (in both cases, the default value is ignored)

In your case, since you are initializing the default value using an attribute of your obj that you don't have at hand at initialization time, you may use a factory instead:

def add_product_form_factory(default_type_name):
    class AddProductForm(Form):
        title = TextField('Title')
        type = QuerySelectField('Type',
            query_factory=lambda: ProductType.query.order_by(ProductType.sequence).all(),
            default=ProductType.query.filter_by(name=default_type_name).one()
        )
    return AddProductForm

Here you have a function that composes the form according to the parameters (in this case you don't need the default being a callable):

AddProductForm = add_product_form_factory(default_type_name='foo')
form = AddProductForm()

ps. the benefits of a form factory is that you can dynamically work with forms, creating fields or setting choices at run time.



回答2:

use parameters like:

QuerySelectField(allow_blank=True, blank_text=u'-- please choose --', ...)

can add default select option