Flask-WTForms

Cho Zin Thet
5 min readSep 24, 2021

--

wtform (vecteezy image)

WTForms protect Cross-Site Request Forgery Attack by creating random token in every time the browser refresh. And it also supports input validation. It is easy to create form, validation and show error messages.

Quickstart

There is 5 steps to use wtform in flask.

1. Installation

pip install WTForms
pip install Flask-WTF

2. create form

main.py

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField
from wtforms.validators import DataRequired, Email, Length
class LoginForm(FlaskForm):
email = StringField(label='email', validators=[DataRequired()])
password = PasswordField(label='password', validators=[DataRequired()])

3. create secret key

app.config['SECRET_KEY'] = 'secret'

4. passing form

main.py

@app.route("/", methods=['GET', 'POST'])
def login():
form = LoginForm()
if request.method == 'POST':
print(request.form['email'])
print(request.form['password'])
return redirect(url_for("success", email=request.form['email']))
return render_template("login.html", form=form)

login.html

<form method="POST" action="{{url_for('login')}}">
{{ form.csrf_token }}
{{ form.email.label }}
{{ form.email }}<br>
{{ form.password.label }}
{{ form.password }}<br>
<button type="submit">Login</button>
</form>

5. Check csrf_token

main.py

@app.route("/", methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
print(request.form['email'])
print(request.form['password'])
return redirect(url_for("success", email=request.form['email']))
return render_template("login.html", form=form)

Create form

Inherit flask wtform when creating our own form and in form class have fields objects and each field create input fields.

main.py

from flask_wtf import FlaskForm
from wtforms import PasswordField
from wtforms.fields.html5 import EmailField
from wtforms.validators import DataRequired, Email, Length
class LoginForm(FlaskForm):
email = EmailField(label='email', validators=[DataRequired()])
password = PasswordField(label='password', validators=[DataRequired()])
wtform works on browser

Csrf_token

There is a hidden input tag with value with csrf_token. That values change when the browser refresh and we only need to check that token in flask application if user actually submit the form.

Check CSRF Token

form.validate_on_submit() function check csrf token. Not only wtform is submitted but also check form validation and csrf token. Submit field never work in wtform until valid csrf token.

@app.route("/", methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
print(request.form['email'])
print(request.form['password'])
return redirect(url_for("success", email=request.form['email']))
return render_template("login.html", form=form)

Fields

Field is the type of input in form. In field object you can pass label, validators and other parameters.

  • Label ➜ label of the fields
  • Validators ➜ validators check input if it is validate. (for example, minium password is 7 characters) wtforms validators

Most useful fields

1. Password Field

<input type=”password”>

from wtforms import PasswordFieldpassword = PasswordField(label='password', validators=[DataRequired()])

get password

form.password.data

2. Text Field

<input type=”text”>

from wtforms import TextFieldname = TextField(label='name', validators=[DataRequired()])

get text input data

form.name.data]

3. BooleanField

<input type=”checkbox”>

from wtforms import BooleanFieldagree = BooleanField(label='agree')

get checkbox data

form.agree.data

4. Text area Field

<textarea></textarea>

from wtforms import TextAreaFieldcomment = TextAreaField(label='comment')

get text area input data

form.comment.data

5. Select Field

<select id="colors" name="colors">
<option value="red">red color</option>
<option value="blue">blue color</option>
<option value="yellow">yellow color</option>
</select>

In select field, choices parameter with list to create multiple values.

choices = [(value, label)]

from wtforms import SelectFieldcolors = SelectField(label='color', choices=[('red', 'red color'), ('blue', 'blue color'), ('yellow', 'yellow color')])

get selected data

form.colors.data

6. Radio Field

<input id="genders-0" name="genders" type="radio" value="female">
<label for="genders-0">female</label>
<input id="genders-1" name="genders" type="radio" value="male">
<label for="genders-1">male</label>

In radio field, choices parameter with list to create multiple values.

choices = [(value, label)]

from wtforms import RadioFieldgenders = RadioField(label='gender', choices=[('female', 'female'), ('male', 'male')])

get radio input data

{% for gender in form.genders %}
<tr>
<td>{{ gender }}</td>
<td>{{ gender.label }}</td>
</tr>
{% endfor %}

7. Email Fields

<input type=”email”>

from wtforms.fields.html5 import EmailFieldemail = EmailField(label='email', validators=[DataRequired()])

get email input data

form.email.data

8. File Field

<input id=”logo” name=”logo” type=”file”>

from werkzeug.utils import secure_filename
from flask_wtf.file import FileField
logo = FileField(label='logo')

get file data

if form.validate_on_submit():
filename = secure_filename(form.logo.data.filename)
return redirect(url_for("success", email=request.form['email']))
else:
filename = None

set the enctype in html form

<form method="POST" action="{{url_for('login')}}" enctype="multipart/form-data">

9. Multiple File Fields

<input id=”images” multiple=”” name=”images” type=”file”>

from wtforms import PasswordField, MultipleFileFieldimages = MultipleFileField(label='images upload')

get multiple files data

form.images.data

Validators

Validators check input if it is valid. (for example, minium password is 7 characters or data required) wtforms validators

  • passing validators as fields parameter
  • error messages with flash messages

1. DataRequired

passing validators as fields parameter

from wtforms.validators import DataRequiredemail = EmailField(label='email', validators=[DataRequired()])

2. Email

  • passing validators as fields parameter (main.py)
from wtforms.validators import DataRequired, Emailemail = EmailField(label='email', validators=[DataRequired(), Email(message='Invalid Email')])
  • errors messages with flash messages (login.html)
{{ form.email.label }}
{{ form.email }}<br>
{% if form.errors %}
<ul class=errors>
{% for error in form.errors['email'] %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
invalid email

3. Length of string

  • passing validators as fields parameter (main.py)
from wtforms.validators import DataRequired, Email, Lengthpassword = PasswordField(label='password', validators=[DataRequired(), Length(min=5, max=20, message="min 5 and max 20")])
  • showing validation errors with flash messages (login.html)
{{ form.password.label }}
{{ form.password }}<br>
{% if form.errors %}
<ul class=errors>
{% for error in form.errors['password'] %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}

4. File Required

  • passing validators as fields parameter (main.py)
from flask_wtf.file import FileField, FileRequiredimage = FileField(label='image upload', validators=[FileRequired()])

--

--