Wtforms 2 Chinese introductory course (crash course)


You snap your fingers and start working on the great Python web application you want to write. After you finish writing some pages, you need to deal with the annoying task: processing and verifying form input. Enter (start) wtforms

But why do I need itthe other oneWhat about the frame? Well, some web application frameworks use a combination of database model and form processing. At the same time, it is very convenient for creating / updating view functions. Maybe every form you need can be directly mapped to a database model. Maybe you have used a general form processing framework, You just want to customize the HTML code generation of these form fields and define your verifier

Using wtforms, you can generate HTML code belonging to your form field. In addition, we allow you to customize it in the template. This allows you to maintain independent code and presentation, and keep these messy parameters outside Python code. Because we strive for loose coupling, you should be able to do so in any template engine you like

Remarks: I have not passed level 4 and my translation level is limited. Please correct me

Download and install

The easiest way to install wtforms is to useeasy_installandpip:

basheasy_install WTForms
# or
pip install WTForms

You can start fromPyPIManualdownloadWtforms and runpython install .

If you are the kind of person who likes all these risks, run the latest version from git, and you can get the latest change setPackaged version, or go toProject Home Clone the code warehouse

Main concepts

  • FormsClass is the core container of wtforms. Forms represent a collection of fields, which can be accessed in the form of dictionary or attribute

  • FieldsDo the most onerous work. Each field represents a data type, and the field operation forces the form to enter that data type. For example,InputRequiredandStringFieldRepresents two different data types. In addition to the data contained in the field, it also contains a large number of useful attributes, such as labels, descriptions, and lists of validation errors

  • Each field has oneWidget(component) instance. The work of a widget is to render the HTML representation of a field. Each field can specify a widget instance, but each field has a reasonable widget by default. Some fields are simple and convenient, such asTextAreaFieldOnly the default widget isTextAreaof

  • To specify validation rules, the field contains a list of validators


Let’s get straight to the point and define our first form:

pythonfrom wtforms import Form, BooleanField, StringField, validators

class RegistrationForm(Form):
    username     = StringField('Username', [validators.Length(min=4, max=25)])
    email        = StringField('Email Address', [validators.Length(min=6, max=35)])
    accept_rules = BooleanField('I accept the site rules', [validators.InputRequired()])

When you create a form, the way you define a field is similar to the way many ORM define their columns: by defining class variables, that is, instances of the field

Because forms are regular Python classes, you can easily extend them to what you expect:

pythonclass ProfileForm(Form):
    birthday  = DateTimeField('Your Birthday', format='%m/%d/%y')
    signature = TextAreaField('Forum Signature')

class AdminProfileForm(ProfileForm):
    username = StringField('Username', [validators.Length(max=40)])
    level    = IntegerField('User Level', [validators.NumberRange(min=0, max=10)])

Through subclasses,AdminProfileFormClass gets the definedProfileFormClass. This allows you to easily share a common subset of fields between different forms. For example, in the above example, we add the admin only field toProfileForm.

Use form

Using a form is as simple as instantiating it. Think of the Django style view function below, which uses the previously definedRegistrationFormClass:

pythondef register(request):
    form = RegistrationForm(request.POST)
    if request.method == 'POST' and form.validate():
        user = User()
        user.username = =
    return render_response('register.html', form=form)

First, we instantiate the form and give it some informationrequest.POSTThen we check whether the request uses post mode. If it is, we verify the form and check that the user complies with these rules. If successful, we create a new requestUserModel, assign data to it from the validated form, and finally save it

Edit existing objects

Our previous registration example showed how to receive input and validate for a new entry, but what if we want to edit an existing object? It’s simple::

pythondef edit_profile(request):
    user = request.current_user
    form = ProfileForm(request.POST, user)
    if request.method == 'POST' and form.validate():
    return render_response('edit_profile.html', form=form)

Here, we provide the form at the same timerequest.POSTAnd user objects to instantiate the form. By doing so, the form willuserObject gets any data that does not appear in the submitted data

We also use formspopulate_objMethod to refill the user object and use the content of the validated form. This method provides convenience when the field name matches the name of the object you provide data. Usually, you will want to assign values manually, but for this simple example, it is the best. It can also be used in curd and admin forms

Explore in the console

The wtforms form is a very simple container object. Perhaps the easiest way to find out what is useful to you in the form is to play with the form in the console:

python>>> from wtforms import Form, StringField, validators
>>> class UsernameForm(Form):
...     username = StringField('Username', [validators.Length(min=5)], default=u'test')
>>> form = UsernameForm()
>>> form['username']
<wtforms.fields.StringField object at 0x827eccc>
>>> form.validate()
>>> form.errors
{'username': [u'Field must be at least 5 characters long.']}

What we see is that when you instantiate a form, the form contains instances of all fields. The access fields can be in the form of dictionary or attribute. These fields have their own attributes, just like a closed form

When we validate a form, it returns a logical false, which means that at least one validation rule is not satisfiedform.errorsWill give you a summary of all errors

python>>> form2 = UsernameForm(username=u'Robert')
{'username': u'Robert'}
>>> form2.validate()

This time, we instantiateUserFormShi GeiusernameBy passing a new value, it is sufficient to validate the form

How do forms get data

In addition to using the first two parameters(formdataandobj)In addition to providing data, you can send keyword parameters to fill in the form. Please note that some parameter names are reserved:formdata, obj, prefix.

formdatathanobjHigh priority,objHigher priority than keyword parameters. For example:

pythondef change_username(request):
    user = request.current_user
    form = ChangeUsernameForm(request.POST, user, username='silly')
    if request.method == 'POST' and form.validate():
        user.username =
        return redirect('change_username')
    return render_response('change_username.html', form=form)

Although you almost never use all three methods together in practice, give an example of how wtforms looks upusernameDomain:

  1. If the form is submitted(request.POSTIn practice, even if there is no form input in this field, if there is any kind of form input, we will process form input

  2. If there is no form entry, try in the following order:

    1. inspectuserIs there a nameusernameProperties of
    2. Check if a file namedusernameKeyword parameters
    3. Finally, if all fail, use the default value of the domain, if any


Validators in wtforms provide a set of validators for fields, which run when the form containing the field is verified. You can provide the validator through the second parameter of the field constructorvalidators:

pythonclass ChangeEmailForm(Form):
    email = StringField('Email', [validators.Length(min=6, max=120), validators.Email()])

You can provide any number of validators for a domain. Usually, you want to provide a custom error message:

pythonclass ChangeEmailForm(Form):
    email = StringField('Email', [
        validators.Length(min=6, message=_(u'Little short for an email address?')),
        validators.Email(message=_(u'That\'s not a valid email address.'))

This is usually better to provide your own message, which is common as a necessary default message. This is also a way to provide localized error messages

For a list of built-in validators, seeValidators.

Render domain

Rendering a domain is as simple as forcing it to be a string:

python>>> from wtforms import Form, StringField
>>> class SimpleForm(Form):
...   content = StringField('content')
>>> form = SimpleForm(content='foobar')
>>> str(form.content)
'<input id="content" name="content" type="text" value="foobar" />'
>>> unicode(form.content)
u'<input id="content" name="content" type="text" value="foobar" />'

However, the real power of the rendering domain comes from its__call__()Method. Calling field, you can provide keyword parameters, which will be injected as HTML attributes in the output

python>>> form.content(style="width: 200px;", class_="bar")
u'<input class="bar" id="content" name="content" style="width: 200px;" type="text" value="foobar" />'

Now, let’s apply this power inJinjaRender the form in the template. First, our form:

pythonclass LoginForm(Form):
    username = StringField('Username')
    password = PasswordField('Password')

form = LoginForm()

Then the template file:

jinja<form method="POST" action="/login">
    <div>{{ form.username.label }}: {{ form.username(class="css_class") }}</div>
    <div>{{ form.password.label }}: {{ form.password() }}</div>

Similarly, if you use Django template, you can use the template tag provided in Django extension when you want to transfer keyword parametersform_field:

django{% load wtforms %}
<form method="POST" action="/login">
        {{ form.username.label }}:
        {% form_field form.username class="css_class" %}
        {{ form.password.label }}:
        {{ form.password }}

These two will output:

html<form method="POST" action="/login">
        <label for="username">Username</label>:
        <input class="css_class" id="username" name="username" type="text" value="" />
        <label for="password">Password</label>:
        <input id="password" name="password" type="password" value="" />

Wtforms is unknown to the template engine and will work with any engine that allows attribute access, string coercion and function call. In Django template, when you can’t pass parameters, the template labelform_fieldProvide convenience

Display error message

Now that our form has a template, let’s add an error message:

jinja<form method="POST" action="/login">
    <div>{{ form.username.label }}: {{ form.username(class="css_class") }}</div>
    {% if form.username.errors %}
        <ul class="errors">{% for error in form.username.errors %}<li>{{ error }}</li>{% endfor %}</ul>
    {% endif %}

    <div>{{ form.password.label }}: {{ form.password() }}</div>
    {% if form.password.errors %}
        <ul class="errors">{% for error in form.password.errors %}<li>{{ error }}</li>{% endfor %}</ul>
    {% endif %}

If you like to display a large string of error messages at the top, it’s also simple:

jinja{% if form.errors %}
    <ul class="errors">
        {% for field_name, field_errors in form.errors|dictsort if field_errors %}
            {% for error in field_errors %}
                <li>{{ form[field_name].label }}: {{ error }}</li>
            {% endfor %}
        {% endfor %}
{% endif %}

Since error handling can become quite lengthy, it is better to use Jinja macros (macros, or the same meaning) in your template to reduce references(example)

Custom verifier

There are two ways to customize a verifier. Define a custom verifier and use it in the domain:

pythonfrom wtforms.validators import ValidationError

def is_42(form, field):
    if != 42:
        raise ValidationError('Must be 42')

class FourtyTwoForm(Form):
    num = IntegerField('Number', [is_42])

Or by providing an in form field specific verifier:

pythonclass FourtyTwoForm(Form):
    num = IntegerField('Number')

    def validate_num(form, field):
        if != 42:
            raise ValidationError(u'Must be 42')

For more complex validators with parameters, seeCustom validatorspart.

next step

The crash course is only a rough introduction to how to start using wtforms to process form input and validation in your application. For more information, you should refer to the following:

  • Wtforms documentHave API documentation for the entire library
  • Solve specific problemsIt can help you deal with the specific integration problems of wtforms and other frameworks
  • mailing listIt’s where you get help, discuss wtforms bugs, and suggest new features

Original English:
Original translation: