WTForms
WTForms is a framework-agnostic library handling web forms in Python: define a form as a class, validate incoming data, render fields in templates.
Note: WTForms is still maintained, but it is a legacy choice at this point. For new projects, prefer your framework’s own forms (Django) or Pydantic-based validation (FastAPI). Kept here as a reference.
Official documentation: WTForms documentation
Define a form
import wtforms
class UrlFilterForm(wtforms.Form):
scheme = wtforms.StringField()
netloc = wtforms.StringField()
path = wtforms.StringField()
query = wtforms.StringField()
fragment = wtforms.StringField()
Handle a request
The same handler serves the empty form (GET) and the submission (POST):
def handle(self, request):
form = MyForm(request.form)
if request.method == 'POST' and form.validate():
# validation succeeded, handle success here
return self.redirect('...')
return self.render_template('...', form=form)
Render a form in a Jinja template
Fields and labels are callable and return safe markup (markupsafe.Markup), so no | safe
filter is needed. Keyword arguments become HTML attributes:
<form class="form-inline" method="post" action="">
{% for field in form %}
<div class="form-group">
{{ field.label }}{% if field.flags.required %}*{% endif %}
{{ field(class='form-control') }}
</div>
{% endfor %}
<button type="submit" class="btn btn-outline-primary">Submit</button>
</form>
To render validation errors, each field exposes an errors list:
<div class="form-group {% if form.some_field.errors %}has-error{% endif %}">
{{ form.some_field.label(class="col-sm-2 control-label") }}
<div class="col-sm-10">
{{ form.some_field(class='form-control') }}
{% if form.some_field.errors %}
<ul class="errors">{% for error in form.some_field.errors %}<li>{{ error }}</li>{% endfor %}</ul>
{% endif %}
</div>
</div>
Override a default once the form is instantiated
Setting form.my_field.default after instantiation has no effect: the default is copied
into the field data at instantiation time. Set the data instead:
form.my_field.data = '...'
Add errors after form validation
field.errors is a plain list after validate(), so you can append to it; the error also
shows up in form.errors:
form.my_field.errors.append('Caramba!')
Verified on WTForms 3.2.2.