I have this order form which allows my users to create an order. An order consists of multiple tuples of (producetype, quantity)
. Producetype should be rendered in a <select>
form while quantity can just be an input. The choices of producetype should be dynamically added because that could change. Currently, I've written this in bare html
I would like to use WTForm for this because WTForm really simplifies my code. However, I am unable to do so:
Code:
class OrderEntryForm(Form):
quantity = IntegerField('Quantity',
[validators.Required(), validators.NumberRange(min=1)])
# we will be dynamically adding choices
producetype = SelectField('Produce',
[validators.Required()],
choices=[])
class OrderForm(Form):
name = TextField('Crop', [validators.Length(min=3, max=60)])
due_date = DateField('Due Date', [validators.required()])
order_entries = FieldList(FormField(OrderEntryForm))
I have the following questions:
order_entries
field of the OrderForm? order = { name:hello, due_date:2014-06-18, order_entries:[ {producetype_id: 1, quantity: 2}, {producetype_id: 3, quantity: 4}] }
, how can populate my OrderForm
with the right OrderEntryForm
values? My code is available here: https://gist.github.com/vicngtor/f8c8f0519dbd6b3b6110
How can I dynamically add choices to the order_entries field of the OrderForm
This depends on what you mean
If you mean you want to add lines items to the form after render. You have to use DOM manipulation to add these on the client side. WTForms has a naming convention for addressing indices on form fields. Its just name="<form-field)name>-<index>"
. If you add a name using javascript that follows this convention WTForms will know how to handle it on the backend.
If you mean you want to have a dynamic choice list then you can just iterate over the FieldList
in the form after its instantiated. You can assign choices
any iterable of 2-tuples. In the example below I am assigning them directly but they could just as easily have been retrieved from storage.
order_form = OrderForm()
for sub_form in order_form.order_entries:
sub_form.producetype.choices = [('2', 'apples'), ('2', 'oranges')]
how can populate my OrderForm with the right OrderEntryForm values?
You can just bind objects directly to the form using the obj
keyword argument. WTForms is smart enough to dynamically build the form from the object's attributes.
from wtforms import Form, IntegerField, SelectField, TextField, FieldList, FormField
from wtforms import validators
from collections import namedtuple
OrderEntry = namedtuple('OrderEntry', ['quantity', 'producetype'])
Order = namedtuple('Order', ['name', 'order_entries'])
class OrderEntryForm(Form):
quantity = IntegerField('Quantity',
[validators.Required(), validators.NumberRange(min=1)])
# we will be dynamically adding choices
producetype = SelectField('Produce',
[validators.Required()],
choices=[
(1, 'carrots'),
(2, 'turnips'),
])
class OrderForm(Form):
name = TextField('Crop', [validators.Length(min=3, max=60)])
order_entries = FieldList(FormField(OrderEntryForm))
# Test Print of just the OrderEntryForm
o_form = OrderEntryForm()
print o_form.producetype()
# Create a test order
order_entry_1 = OrderEntry(4, 1)
order_entry_2 = OrderEntry(2, 2)
order = Order('My First Order', [order_entry_1, order_entry_2])
order_form = OrderForm(obj=order)
print order_form.name
print order_form.order_entries
The above example creates a sample Order
and supplies it to the obj
keyword. On render this will generate the following(unstyled):