Reputation: 436
I have a problem with setting default values of a FieldForm in WTForms.
models.py
class RepairCategory(db.Model): name = ... class Repair(db.Model): price = .. category_id [FK] = ... product_id [FK] class Product(db.Model): name = ... description = ... color = ...
ProductBase contains only attributes which match the Product db Model.
forms.py
class NewRepair(Form):
#this is okay - it get's populated
repair_category = QuerySelectField("Repair category",
query_factory=get_categories)
price = DecimalField()
class ProductBase(Form):
name = StringField("Name ", validators=[DataRequired(), Length(1, 64)])
color = StringField("Color ", validators=[DataRequired(), Length(1, 64)])
description = TextAreaField("Description")
active = BooleanField()
class Product(Form):
base_product = FormField(ProductBase)
add_repairs = FormField(NewRepair)
submit = SubmitField('Submit')
The add_repairs contains a form which I want to use in the view to create Repairs. The base_product is a form to which, ideally, I want to pass a obj=product in the views, so the default values get populated automatically. I want to use the form.populate_obj() as well, just on the base_product form.
here's how I create the Product form in the view:
def make_product_form(form=None, product=None, **kwargs):
form = form()
form.base_product.obj = product
return form
And then, when handling POSTs, I want to do:
def product(id):
product = Product.query.get_or_404(id)
form = make_product_form(form=Product,product=product)
if form.validate_on_submit():
product_form = form.base_product
product_form.populate_obj(product)
However, the base_form from the Product form, doesn't get filled with default values from an existing object.
Any suggestions on how to achieve this? Thanks :)
Upvotes: 2
Views: 3568
Reputation: 436
After discovering that you can run code directly from the Flask's stack trace in the browser(how cool is that?), I found a solution to my problem.
The key is that when creating the main Product form(in make_product_form()), when I do
form.base_product.obj
I don't really access the obj attribute.
However, doing form.base_product.form.process(obj=product)
did the trick! The key is using the base_product.form in order to get access to the form within the FormField.
Here are all the attribututes of form.base_product. The dir() is evaluated just after the form=form() in the make_product_form():
dir(form.base_product)
[#ommitted some attributes#,
'__weakref__', '_formfield', '_obj', '_run_validation_chain',
'_translations', 'data', 'default', 'description',
'do_not_call_in_templates', 'errors', 'filters', 'flags', 'form',
'form_class', 'gettext', 'id', 'label', 'meta', 'name', 'ngettext',
'object_data', 'populate_obj', 'post_validate', 'pre_validate',
'process', 'process_data', 'process_errors', 'process_formdata',
'raw_data', 'render_kw', 'separator', 'short_name', 'type',
'validate', 'validators', 'widget' ]
What this shows is that actually form.base_product is a Field, not a Form,and doing form.base_product.form got me the ProductBase form.
Hope this is helpful
**Update**
I had to use the process(obj=product) only on GET requests, to prepopulate the form, otherwise on POST, the actual form data is discarded.
Upvotes: 0
Reputation: 3010
Use form process
method to populate form fields with object's attributes values.
Use form populate_obj
method to populate object's attributes with values from form fields.
Note: names of object's attributes must match names with form fields.
process
example:
>>> class MyObj(object):
... name = "object's name"
>>> from wtforms import Form, StringField
>>> class MyForm(Form):
... name = StringField("Form's name")
>>> my_obj = MyObj()
>>> my_obj.name
"object's name"
>>> my_form = MyForm()
>>> print my_form.name.data
None
>>> my_form.process(obj=my_obj)
>>> my_form.name.data
"object's name"
populate_obj
example:
>>> my_form.name.data = "Form's name"
>>> my_form.name.data
"Form's name"
>>> my_obj.name
"object's name"
>>> my_form.populate_obj(my_obj)
>>> my_obj.name
"Form's name"
Upvotes: 2