# Creating a form with WTForms
TIP
List of all code changes made in this lecture: https://diff-store.com/diff/section14__06_create_form_with_wtforms (opens new window)
# Dependencies
To use WTForms in a Flask application, the easiest thing to do is install two dependencies: wtforms
and flask-wtf
.
WTForms will help us define the forms, and Flask-WTF will add extra functionality such as that to deal with CSRF tokens.
pip install wtforms
pip install flask-wtf
# What is a CSRF token?
Cross-Site Request Forgery (CSRF)[1] is a web security vulnerability. It allows an attacker to trick the browser into submitting a form that sends data to your application, without the user noticing it.
By including a unique token generated by the Flask server in all requests, it prevents the possibility of a CSRF attack because the attacker will need the server to generate the form, which comes with the whole HTML page and usually requires the user to log-in first.
# Create a WTForm Class
Let's create a new file called movie_library/forms.py
. There, we will create a Python class that represents our form.
from flask_wtf import FlaskForm
class MovieForm(FlaskForm):
pass
Note that the import is from flask_wtf
, as that will give the form CSRF protection (as long as the necessary field is included in the rendered HTML, more on that later).
# Define Form Fields
Next, we need to define our form fields. This form is for creating new movies, so it will only include four fields: title
, director
, year
, and submit
. The first two will be strings, and the year
will be an integer. The submit
field will be rendered as a button to submit the form.
Giving our fields appropriate types will do two things:
- Render the appropriate field type in HTML
- Make validation easier
from flask_wtf import FlaskForm
from wtforms import IntegerField, StringField, SubmitField
class MovieForm(FlaskForm):
title = StringField("Title")
director = StringField("Director")
year = IntegerField("Year")
submit = SubmitField("Add Movie")
With just this, we can go and render the form and it will work just fine!
However, one of the key benefits of WTForms is how easy it is to include validation in our forms. Let's look at that.
# Data Validation with WTForms
We will be including two types of validation in this form:
InputRequired
, which makes it so the field cannot be empty when submittedNumberRange
, which makes it so the field must have a value between two provided numbers
WTForms ships with many more validators[2] that you can use.
For now though, let's add the validation to our fields:
from flask_wtf import FlaskForm
from wtforms import IntegerField, StringField, SubmitField
from wtforms.validators import InputRequired, NumberRange
class MovieForm(FlaskForm):
title = StringField("Title", validators=[InputRequired()])
director = StringField("Director", validators=[InputRequired()])
year = IntegerField(
"Year",
validators=[
InputRequired(),
NumberRange(min=1878, message="Please enter a year in the format YYYY."),
],
)
submit = SubmitField("Add Movie")
I've made all three input fields required, and I've made the year
field have a minimum value of 1878, since that's when the first movie was released.
A few things to note:
validators
is a keyword argument which takes a list of validator objects.- Each validator object takes an optional
message
keyword argument which will be displayed to users if the validation fails at that step. - Validators are validated in the order in which they are defined, so that's why I put
InputRequired()
first.
Now that we've defined our form using WTForms, a few questions still remain!
- How do we render this form in our template?
- How do we receive form data?
- How do we validate the form data?
- If validation fails, how do we display the error messages to the user?
We will tackle all these questions over the next 2 lectures. Let's go!