mirror of https://github.com/mitsuhiko/flask.git
Armin Ronacher
15 years ago
6 changed files with 115 additions and 10 deletions
@ -0,0 +1,109 @@
|
||||
Form Validation with WTForms |
||||
============================ |
||||
|
||||
When you have to work with form data submitted by a browser view code |
||||
quickly becomes very hard to read. There are libraries out there designed |
||||
to make this process easier to manage. One of them is WTForms which we |
||||
will handle here. If you find yourself in the situation of having many |
||||
forms, you might want to give it a try. |
||||
|
||||
When you are working with WTForms you have to define your forms as classes |
||||
first. I recommend breaking up the application into multiple modules |
||||
(:ref:`larger-applications`) for that and adding a separate module for the |
||||
forms. |
||||
|
||||
The Forms |
||||
--------- |
||||
|
||||
This is an example form for a typical registration page:: |
||||
|
||||
from wtforms import Form, BooleanField, TextField, validators |
||||
|
||||
class RegistrationForm(Form): |
||||
username = TextField('Username', [validators.Length(min=4, max=25)]) |
||||
email = TextField('Email Address', [validators.Length(min=6, max=35)]) |
||||
password = PasswordField('New Password', [Required(), |
||||
EqualTo('confirm', mesage='Passwords must match')]) |
||||
confirm = PasswordField('Repeat Password') |
||||
accept_tos = BooleanField('I accept the TOS', [validators.Required()]) |
||||
|
||||
In the View |
||||
----------- |
||||
|
||||
In the view function, the usage of this form looks like this:: |
||||
|
||||
@app.route('/register', methods=['GET', 'POST']) |
||||
def register(): |
||||
form = RegistrationForm(request.form) |
||||
if request.method == 'POST' and form.validate(): |
||||
user = User(form.username.data, form.email.data, |
||||
form.password.data) |
||||
db_session.add(user) |
||||
flash('Thanks for registering') |
||||
redirect(url_for('login')) |
||||
return render_template('register.html', form=form) |
||||
|
||||
Notice that we are implying that the view is using SQLAlchemy here |
||||
(:ref:`sqlalchemy-pattern`) but this is no requirement of course. Adapt |
||||
the code as necessary. |
||||
|
||||
Things to remember: |
||||
|
||||
1. create the form from the request :attr:`~flask.request.form` value if |
||||
the data is submitted via the HTTP `POST` method and |
||||
:attr:`~flask.request.args` if the data is submitted as `GET`. |
||||
2. to validate the data, call the :func:`~wtforms.form.Form.validate` |
||||
method which will return `True` if the data validates, `False` |
||||
otherwise. |
||||
3. to access individual values from the form, access `form.<NAME>.data`. |
||||
|
||||
Forms in Templates |
||||
------------------ |
||||
|
||||
Now to the template side. When you pass the form to the templates you can |
||||
easily render them there. Look at the following example template to see |
||||
how easy this is. WTForms does half the form generation for us already. |
||||
To make it even nicer, we can write a macro that renders a field with |
||||
label and a list of errors if there are any. |
||||
|
||||
Here an example `_formhelpers.html` template with such a macro: |
||||
|
||||
.. sourcecode:: html+jinja |
||||
|
||||
{% macro render_field(field) %} |
||||
<dt>{{ field.label }} |
||||
<dd>{{ field(**kwargs)|safe }} |
||||
{% if field.errors %} |
||||
<ul class="errors"> |
||||
{% for error in field.errors %} |
||||
<li>{{ error }}</li> |
||||
{% endfor %} |
||||
</ul> |
||||
{% endif %} |
||||
</dd> |
||||
{% endmacro %} |
||||
|
||||
This macro accepts a couple of keyword arguments that are forwarded to |
||||
WTForm's field function that renders the field for us. They keyword |
||||
arguments will be inserted as HTML attributes. So for example you can |
||||
call ``render_field(form.username, class='username')`` to add a class to |
||||
the input element. Note that WTForms returns standard Python unicode |
||||
strings, so we have to tell Jinja2 that this data is already HTML escaped |
||||
with the `|safe` filter. |
||||
|
||||
Here the `register.html` template for the function we used above which |
||||
takes advantage of the `_formhelpers.html` template: |
||||
|
||||
.. sourcecode:: html+jinja |
||||
|
||||
{% from "_formhelpers.html" import render_field %} |
||||
<form method="POST" action="/register"> |
||||
<dl> |
||||
{{ render_field(form.username) }} |
||||
{{ render_field(form.email) }} |
||||
{{ render_field(form.password) }} |
||||
{{ render_field(form.confirm) }} |
||||
{{ render_field(form.accept_tos) }} |
||||
</dl> |
||||
<p><input type=submit value=Register> |
||||
</form> |
Loading…
Reference in new issue