WTForms (https://github.com/wtforms/wtforms) is a standalone robust form handling library that allows you to generate HTML forms from form-like classes, implement fields and form validation, and include cross-source forgery protection (a nasty vulnerability that crackers may try to exploit in your Web applications). We certainly don't want that!
First, to install WTForms library, use the following:
pip install wtforms
Now let's write some forms. A WTForms form is a class that extends the Form
class. As plain as that! Let's create a login form that could be used with our previous login example:
from wtforms import Form, StringField, PasswordField class LoginForm(Form): username = StringField(u'Username:') passwd = PasswordField(u'Password:')
In the preceding code, we have a form with two fields, username
and passwd
, with no validation. It is just enough to build a form in a template, like this:
<form method='post'> {% for field in form %} {{ field.label }} {{ field }} {% if field.errors %} {% for error in field.errors %} <div class="field_error">{{ error }}</div> {% endfor %} {% endif %} {% endfor %} </form>
As seen in the preceding code, you can iterate over the fields of a WTForms form and each field has a few useful attributes you can use to make your HTML look good, such as label
and errors
. {{ field }}
will render a plain HTML input element for you. There are cases where you may want to set special attributes for the input element—for example, required
, which tells your browser that the given field should not be submitted if empty. Call field
as a function in order to achieve that, like so:
{% if field.flags.required %} {{ field(required='required') }} {% endif %}
You could pass any desired argument, as placeholder
or alt
, in line with the example. Flask-Empty (https://github.com/italomaia/flask-empty) has a nice example within its macros.
WTForms uses a flag system in order to allow you to check when some validations are applied to a field. If a field has a "required" validation rule, a required
flag would be set to true in the fields.flags
attribute. But how does WTForms validation work?
In Flask, a validator is a callable you add to your validators
field, or a class method in the format validate_<field>(form, field)
. It allows you to validate that the field data is as required or it raises a ValidationError
explaining what went wrong. Let's see how our nice login form example would look with some validation:
# coding:utf-8 from wtforms import Form, ValidationError from wtforms import StringField, PasswordField from wtforms.validators import Length, InputRequired from werkzeug.datastructures import MultiDict import re def is_proper_username(form, field): if not re.match(r"^w+$", field.data): msg = '%s should have any of these characters only: a-z0-9_' % field.name raise ValidationError(msg) class LoginForm(Form): username = StringField( u'Username:', [InputRequired(), is_proper_username, Length(min=3, max=40)]) password = PasswordField( u'Password:', [InputRequired(), Length(min=5, max=12)]) @staticmethod def validate_password(form, field): data = field.data if not re.findall('.*[a-z].*', data): msg = '%s should have at least one lowercase character' % field.name raise ValidationError(msg) # has at least one uppercase character if not re.findall('.*[A-Z].*', data): msg = '%s should have at least one uppercase character' % field.name raise ValidationError(msg) # has at least one number if not re.findall('.*[0-9].*', data): msg = '%s should have at least one number' % field.name raise ValidationError(msg) # has at least one special character if not re.findall('.*[^ a-zA-Z0-9].*', data): msg = '%s should have at least one special character' % field.name raise ValidationError(msg) # testing our form form = LoginForm(MultiDict([('username', 'italomaia'), ('password', 'lL2m@msbb')])) print form.validate() print form.errors
In the preceding code, we have a full form example, with validation, using classes, methods and functions as validators and a simple test. The first argument for each of our fields is the field label. The second argument is a list of validators you want run when the form.validate
method is called (that's pretty much what form.validate
does). Each field validator is run sequentially, raising a ValidationError
(and stopping the validation chain call) if an error is found.
Each validator receives the form and field as arguments and must do the validating thing with them. As seen with validate_password
, which is called for the field password
because of the naming convention. field.data
holds the field input, so you can just validate that most of the time.
Let's understand each validator:
Length
: This validates that the input value length is within a given range (min, max).InputRequired
: This validates that the field received a value, any value.is_proper_username
: This validates that the field value matches a given regex. (There is also a built-in validator to match a regex to a given value, called Regexp. You should try it.)validate_password
: This validates that the field value matches a given group of regex rules.In our example test, you may have noticed the use of a special dictionary-like class called MultiDict
from the werkzeug
library. It is used because the formdata
parameter, which may receive your request.form
or request.args
, must be a multidict-type
. It pretty much means you can't use a plain dictionary here.
When form.validate
is called, all the validators are called. First the field validators, then the class
method field validators; form.errors
is a dictionary populated with all the field errors found after validate is called. You can then iterate over it to show what you found in your templates, console, and so on.
18.222.179.186