WTForms and you

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.179.186