Basic tutorial on using the WTForms form framework in Python Flask, flaskwtforms

Source: Internet
Author: User
Tags valid email address

Basic tutorial on using the WTForms form framework in Python Flask, flaskwtforms

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']<wtforms.fields.StringField object at 0x827eccc>>>> 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)'<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. 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'<input class="bar" id="content" name="content" style="width: 200px;" type="text" value="foobar" />'

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:

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

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 %}<form method="POST" action="/login"> <div>  {{ form.username.label }}:  {% form_field form.username class="css_class" %} </div> <div>  {{ form.password.label }}:  {{ form.password }} </div></form>

The two will output:

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

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 ::

<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 %}</form>

If you like to display a large string of error messages on the top, it is also very simple:

{% 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 %} </ul>{% 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:

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.