There is a real lack of documentation on how to work with WTForms' FieldList. So thanks to the internet I've been able to hack together the following:
Form:
class BranchForm(Form):
name = StringField('Name', validators = [Required()])
equipment = FieldList(SelectField('Equipment', validators=[Required()], coerce=int,
choices = [(x.id, x.name) for x in Equipment.query.all()]))
mod = FieldList(StringField('Method of Delivery', validators = [Optional()]))
View:
def edit_branch(id):
branch = Branch.query.filter_by(id=id).first()
#populate data_in to be used by BranchForm
data_in = []
for eq_obj in branch.equipment_assoc:
data_in.append(('equipment', eq_obj.equipment.id))
data_in.append(('mod', eq_obj.mod))
editform = BranchForm(data=MultiDict(data_in))
if editform.validate_on_submit():
branch.name = editform.name.data
db.session.add(branch)
db.session.commit()
return redirect('/admin/branches/' + str(branch.id))
editform.name.data = branch.name
return render_template("branch_edit.html",
title="Edit Branch",
branch = branch,
editform = editform)
What's throwing me off is that everywhere else that I've used a WTForm Form and populated the fields with data from my db (like for edit forms), I've had to populate these form fields after the form.validate_on_submit() block, because if not, then the form will never update as whatever is submitted is immediately overwritten.
See "editform.name.data = branch.name" (this is how I've always done it)
From every example I've found online about populating a FieldList, it apparently must be done during instantiation, but the form has to be instantiated before the validate_on_submit() as well because validate_on_submit() is a method of the form object.
See "editform = BranchForm(data=MultiDict(data_in))" (this is how I've seen FieldLists populated in all the examples I've seen.)
How can I go about populating my form with its field lists?
Alright, so a buddy helped me figure this one out. Here's what I ended up with:
Form:
class BranchForm(Form):
name = StringField('Name', validators = [Required()])
equipment = FieldList(SelectField('Equipment', validators=[Required()], coerce=int,
choices = [(x.id, x.name) for x in Equipment.query.all()]))
mod = FieldList(StringField('Method of Delivery', validators = [Optional()]))
def populate_assoc(self, branch_obj):
i = 0
branch_obj.name = self.name.data
for assoc_obj in branch_obj.equipment_assoc:
assoc_obj.equipment_id = self.equipment[i].data
assoc_obj.mod = self.mod[i].data
i += 1
View:
def edit_branch(id):
branch = Branch.query.filter_by(id=id).first()
if request.method == 'POST':
editform = BranchForm()
if editform.validate_on_submit():
editform.populate_assoc(branch)
db.session.add(branch)
db.session.commit()
return redirect('/admin/branches/' + str(branch.id))
#populate data_in to be used
data_in = []
for eq_obj in branch.equipment_assoc:
data_in.append(('equipment', eq_obj.equipment.id))
data_in.append(('mod', eq_obj.mod))
editform = BranchForm(data=MultiDict(data_in))
editform.name.data = branch.name
return render_template("branch_edit.html",
title="Edit Branch",
branch = branch,
editform = editform)
The trick was really to step away from using form.validate_on_submit() as my logic separator, since it relies on the form object. His idea was to use the if request.method == 'POST': for this purpose. This way I can instantiate my form in two different ways. One gets populated for display, the other is only instantiated if the request method is POST, thus retaining the information submitted in the form.
To finish the job I added the populate_assoc method to my form class so that I can easily place the information from the form into my association model.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With