I am trying to populate a form in jinja2 using form fields that are in a dictionary.
#forms.py
class MyForm(FlaskForm):
name = StringField('New Name', validators=[DataRequired()])
fields = {}
fields['Field1'] = StringField('Field 1', validators=[DataRequired()])
fields['Field2'] = StringField('Field 2', validators=[DataRequired()])
#routes.py
@app.route('/test', methods=['GET', 'POST'])
def test():
form = MyForm()
return render_template('_test.html', form=form)
My python code is similar to the above code. If I try to insert the name
field in jinja2 it works fine.
{{ form.name.label(class="form-control-label form-control-sm") }}
However, I don't know how to do the same for a field in the fields
dictionary. If I use the following, it gives me an error.( jinja2.exceptions.UndefinedError: 'wtforms.fields.core.UnboundField object' has no attribute 'label'
)
{{ form.fields['Field1'].label(class="form-control-label form-control-sm") }}
Is it possible to use a dictionary the way I tried to use it or is there an alternative if I have large amount of fields. My goal in using a dictionary was to use a jinja2 loop to iterate over the dictionary elements to insert all the fields without typing one by one.
WTForms doesn't support having Fields defined within a class level dictionary attribute. The Form base class uses FormMeta (from the same file) as a metaclass to identify unbound WTForm Fields in the class definition and bind them to the current Form and this only discovers class level attributes.
A minimal working example using a form factory function is below.
The extra form fields are passed in as a lambda function via the field_factory
parameter so that we can delay the creation of the extra fields until after the name
field is created (although any callable that returns a dict will work). This is needed as WTForms sorts the fields by creation order and not by the order they are supplied in the items
dict inside the make_form()
function.
You can then render them out by iterating over the form instead of specifying a manual order.
Tested on python 3.7.1.
from flask import Flask
from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired
app = Flask(__name__)
app.config['SECRET_KEY'] = "secret"
def make_form(field_factory, name="MyForm"):
items = dict(name=StringField('New Name', validators=[DataRequired()]),
**field_factory())
my_form = type(name, (FlaskForm,), items)
return my_form
if __name__ == "__main__":
with app.test_request_context("/"):
my_form = make_form(field_factory=lambda: dict(
Field1=StringField('Field 1', validators=[DataRequired()]),
Field2=StringField('Field 2', validators=[DataRequired()])
))
form = my_form()
for field in form:
print(field())
Output
<input id="name" name="name" required type="text" value="">
<input id="Field1" name="Field1" required type="text" value="">
<input id="Field2" name="Field2" required type="text" value="">
<input id="csrf_token" name="csrf_token" type="hidden" value="IjY5ZWMyNWYxYzg3MzU2MTM1MGMyMTI0OTNiOGY1ZTk4OWFkZWU2Y2Qi.XMNLww.uvari0GZi4weboIecdtv9Vl8Jvg">
You can't do this. Fields need to be defined directly in the form itself.
But you don't need to. If you only want to iterate over the fields, you can just do that, as shown in the WTForms docs .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.