WTForms are written in Python and are generated for forms. It provides many practical APIs for creating Web forms and works with the Flask framework to pull results, here we will take a look at the basic tutorial on using the WTForms form framework in Python Flask.
Download and Install
The simplest way to install WTForms is to use easy_install and pip:
easy_install WTForms# orpip install WTForms
You can manually download WTForms from PyPI and run python setup. py install.
If you like all these risks, run the latest version from Git. you can get the package version of the latest change set, or go to the project homepage to clone the code repository.
Main concepts
The Forms class is the core container of WTForms. Forms (Forms) indicates a set of Fields. Fields can be accessed in the form of dictionary or attribute form.
Fields (domain) does the most heavy work. each field represents a data type, and the field operation forces the form input to be of that data type. for example, InputRequired and StringField indicate two different data types. in addition to data, a domain also contains a large number of useful attributes, such as tags, descriptions, and List of verification errors.
Each field has a Widget instance. the Widget is used to render the HTML representation of the field. you can specify a Widget instance for each domain, but each domain has a reasonable widget by default. some fields are simple and convenient. for example, TextAreaField is only the default component (widget) of TextArea.
StringField.
To specify a verification rule, the domain contains the Validators list.
Start
Let's go directly to the question and define our first form ::
from wtforms import Form, BooleanField, StringField, validatorsclass 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 field method is similar to that defined by many ORM columns: defines class variables, that is, the instance of the domain.
Because forms are common Python classes, you can easily extend them into what you expect ::
class 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 the subclass, the AdminProfileForm class obtains all the fields of the defined ProfileForm class. this allows you to easily share the common subset of fields between different forms. for example, in the preceding example, we add the admin-only domain to ProfileForm.
Use form
Using a form is as simple as instantiating It. think about the django-style view function below, which uses the previously defined RegistrationForm class ::
def register(request): form = RegistrationForm(request.POST) if request.method == 'POST' and form.validate(): user = User() user.username = form.username.data user.email = form.email.data user.save() redirect('register') return render_response('register.html', form=form)
First, we instantiate the form and provide it with some request. available data in POST. then we check whether the request uses the POST method. if it is, we verify the form and check that the user complies with these rules. if it succeeds, we create a new User model, assign data to it from the verified form, and save it.
Edit existing object
Our previous registration example shows how to receive input and verify for a new entry, but what if we want to edit an existing object? Simple ::
def edit_profile(request): user = request.current_user form = ProfileForm(request.POST, user) if request.method == 'POST' and form.validate(): form.populate_obj(user) user.save() redirect('edit_profile') return render_response('edit_profile.html', form=form)
Here, we provide both request. POST and user objects to instantiate the form. by doing so, the form will obtain any data that does not appear in the submitted data from the user object.
We also use the populate_obj method of the form to re-fill the user object and use the content of the verified form. this method is convenient when the field name matches the name of the object you provide data. generally, you want to manually assign a value, but for this simple example, it is the best. it can also be used for CURD and admin forms.
Explore in the console
WTForms are very simple container objects. perhaps the easiest way to find out which form is useful to you is to play with the form in the console:
>>> from wtforms import Form, StringField, validators>>> class UsernameForm(Form):... username = StringField('Username', [validators.Length(min=5)], default=u'test')...>>> form = UsernameForm()>>> form['username']
>>> form.username.datau'test'>>> form.validate()False>>> form.errors{'username': [u'Field must be at least 5 characters long.']}
We can see that when you instantiate a form, the form contains instances of all fields, and the access fields can be in dictionary or attribute form. these fields have their own attributes, just like closed forms.
When we verify the form, it returns logical false, which means at least one Validation rule does not meet. form. errors will give you a summary of all the errors.
>>> form2 = UsernameForm(username=u'Robert')>>> form2.data{'username': u'Robert'}>>> form2.validate()True
This time, when we instantiate UserForm, we send a new value to username to verify that the form is sufficient.
How to obtain data from a form
In addition to providing data using the first two parameters (formdata and obj), you can transmit keyword parameters to fill the form. Note that some parameter names are reserved: formdata, obj, and prefix.
Formdata has a higher priority than obj and obj has a higher priority than keyword parameters. for example:
def change_username(request): user = request.current_user form = ChangeUsernameForm(request.POST, user, username='silly') if request.method == 'POST' and form.validate(): user.username = form.username.data user.save() return redirect('change_username') return render_response('change_username.html', form=form)
Although you have never used all three methods in practice, the following example shows how WTForms searches for the username field:
If the form is submitted (request. POST is not empty. in practice, even if this field does not have form input, and there are any types of form input, we will process form input.
If there is no form input, try the following order:
Check whether the user has a property named username.
Check whether a keyword parameter named username is provided.
Finally, if both fail, use the default value of the domain, if any.
Validators
Validators in WTForms provides a set of Validators for fields that run when the form containing the fields are verified. you provide the validators to use the second parameter validators of the domain constructor:
class ChangeEmailForm(Form): email = StringField('Email', [validators.Length(min=6, max=120), validators.Email()])
You can provide any number of validators for a domain. Generally, you want to provide a custom error message:
class 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 usually provides your own messages better and is common as necessary default messages. this is also a method for providing localized error messages.
For the list of built-in Validators, refer to Validators.
Rendering domain
Rendering the domain is as simple as forcing it to be a string:
>>> from wtforms import Form, StringField>>> class SimpleForm(Form):... content = StringField('content')...>>> form = SimpleForm(content='foobar')>>> str(form.content)''>>> unicode(form.content)u''
However, the real power of the rendering domain comes from its _ call _ () method. you can provide keyword parameters for calling (calling) fields. they are injected as HTML attributes in the output.
>>> form.content(style="width: 200px;", class_="bar")u''
Now, let's apply this force to render the form in the Jinja template. First, our form:
class LoginForm(Form): username = StringField('Username') password = PasswordField('Password')form = LoginForm()
Then there is the template file:
Similarly, if you use the Django template, you can use the template tag form_field provided in the Django extension when you want to transmit keyword parameters:
{% load wtforms %}
The two will output:
WTForms is unknown to the template engine and works with any engine that allows attribute access, string forcing (string coercion), and function call. in the Django template, when you cannot transmit parameters, the template tag form_field provides convenience.
Show error message
Now our form has a template. let's add an error message ::
If you like to display a large string of error messages on the top, it is also very simple:
{% if form.errors %}
{% for field_name, field_errors in form.errors|dictsort if field_errors %} {% for error in field_errors %}
- {{ form[field_name].label }}: {{ error }}
{% endfor %} {% endfor %}
{% endif %}
Since error handling will become quite lengthy, it is better to use the Jinja macro (macros, or something of the same meaning) in your template to reduce references. (example)
Custom validators
There are two ways to customize the validator. by defining a custom validator and using it in the domain:
from wtforms.validators import ValidationErrordef is_42(form, field): if field.data != 42: raise ValidationError('Must be 42')class FourtyTwoForm(Form): num = IntegerField('Number', [is_42])
Alternatively, you can provide an in-form field-specific validator for a specific field in the form:
class FourtyTwoForm(Form): num = IntegerField('Number') def validate_num(form, field): if field.data != 42: raise ValidationError(u'Must be 42')
Compile a WTForm extension example
class TagListField(Field): widget = TextInput() def _value(self): if self.data: return u', '.join(self.data) else: return u'' def process_formdata(self, valuelist): if valuelist: self.data = [x.strip() for x in valuelist[0].split(',')] else: self.data = []
According to the code above, convert the string in TagListField to the Tag object defined in models. py:
Class TagListField (Field): widget = TextInput () def _ init _ (self, label = None, validators = None, ** kwargs): super (TagListField, self ). _ init _ (label, validators, ** kwargs) def _ value (self): if self. data: r = u'' for obj in self. data: r + = self. obj_to_str (obj) return u'' else: return u'' def process_formdata (self, valuelist): print 'process _ formdata .. 'print valuelist if valuelist: tags = self. _ remove_duplicates ([x. strip () for x in valuelist [0]. split (',')]) self. data = [self. str_to_obj (tag) for tag in tags] else: self. data = None def pre_validate (self, form): pass @ classmethod def _ remove_duplicates (cls, seq): "" de-duplicated "d ={} for item in seq: if item. lower () not in d: d [item. lower ()] = True yield item @ classmethod def str_to_obj (cls, tag): "" convert string to obj object "" tag_obj = Tag. query. filter_by (name = tag ). first () if tag_obj is None: tag_obj = Tag (name = tag) return tag_obj @ classmethod def obj_to_str (cls, obj ): "converts an object to a string" if obj: return obj. name else: return u'' class TagListField (Field): widget = TextInput () def _ init _ (self, label = None, validators = None, ** kwargs ): super (TagListField, self ). _ init _ (label, validators, ** kwargs) def _ value (self): if self. data: r = u'' for obj in self. data: r + = self. obj_to_str (obj) return u'' else: return u'' def process_formdata (self, valuelist): print 'process _ formdata .. 'print valuelist if valuelist: tags = self. _ remove_duplicates ([x. strip () for x in valuelist [0]. split (',')]) self. data = [self. str_to_obj (tag) for tag in tags] else: self. data = None def pre_validate (self, form): pass @ classmethod def _ remove_duplicates (cls, seq): "" de-duplicated "d ={} for item in seq: if item. lower () not in d: d [item. lower ()] = True yield item @ classmethod def str_to_obj (cls, tag): "" convert string to obj object "" tag_obj = Tag. query. filter_by (name = tag ). first () if tag_obj is None: tag_obj = Tag (name = tag) return tag_obj @ classmethod def obj_to_str (cls, obj ): "converts an object to a string" if obj: return obj. name else: return u''
This step mainly processes the form data in process_formdata and converts the string to the required data. Finally, the form can be defined in forms. py as follows:
... Class ArticleForm (Form): "edit article Form" title = StringField (u'title', validators = [Required ()]) category = QuerySelectField (u 'category', query_factory = get_category_factory (['id', 'name']), get_label = 'name') tags = TagListField (u 'tag ', validators = [Required ()]) content = PageDownField (u 'body', validators = [Required ()]) submit = SubmitField (u'release ')...... class ArticleForm (Form): "edit article Form" title = StringField (u'title', validators = [Required ()]) category = QuerySelectField (u 'category', query_factory = get_category_factory (['id', 'name']), get_label = 'name') tags = TagListField (u 'tag ', validators = [Required ()]) content = PageDownField (u 'body', validators = [Required ()]) submit = SubmitField (u'release ')... in views. in py, it is very convenient to process the form: def edit_article (): "edit article" form = ArticleForm () if form. validate_on_submit (): article = Article (title = form. title. data, content = form. content. data) article. tags = form. tags. data article. category = form. category. data try: db. session. add (article) db. session. commit () commit T: db. session. rollback () return render_template ('dashboard/edit.html ', form = form) def edit_article (): "edit article" form = ArticleForm () if form. validate_on_submit (): article = Article (title = form. title. data, content = form. content. data) article. tags = form. tags. data article. category = form. category. data try: db. session. add (article) db. session. commit () commit T: db. session. rollback () return render_template ('dashboard/edit.html ', form = form)
Is the code concise? ^_^...
Of course, writing a complete WTForms extension is still very troublesome. This is just getting started. You can refer to the official extended QuerySelectField source code...
Effect:
For more articles about using the WTForms form framework in Python Flask, refer to the PHP Chinese website!