Heads up! To view this whole video, sign in with your Courses account or enroll in your free 7-day trial. Sign In Enroll
Preview
Start a free Courses trial
to watch this video
Using code-based forms to create HTML forms and also provide data validation gives us two powerful tools for building our web application.
In Flask and in Django, people often get
the wrong idea about forms.
0:00
They hear the word form, and immediately
think about HTML forms.
0:04
They think that forms are all about
display.
0:07
And really that makes forms very limited.
0:10
Forms are about validation.
0:12
Making sure that your data matches a
certain pattern.
0:14
The de facto form library for Flask is
Flask-WTF,
0:17
and it builds on top of an older package
named WTForms, and
0:20
we'll actually end up with both of them
installed.
0:24
You'll install this with pip install
flask-wtf.
0:27
This package also provides us with CSRF,
or cross-site request forgery, protection.
0:31
What's cross-site request forgery?
0:37
Well, imagine you've logged in to your
bank site and
0:39
it keeps you logged in through a cookie.
0:41
Now, imagine that some horrible person out
there sends you an image in an HTML page.
0:43
But instead of an image URL, the URL goes
to some send me $1,000 page on your bank.
0:46
Without CSRF your bank trusts that you
actually made that request, and
0:52
goes ahead and sends them the 1,000 bucks.
0:55
CSRF includes a custom one time code with
each submission.
0:58
And, if the form doesn't have that code,
or
1:02
doesn't have the right one, the request is
ignored.
1:04
Okay, let's make our registration form.
1:07
All right, so if we're gonna forms,
1:10
our app.py, I can already tell it's gonna
get pretty crowded.
1:11
So, we probably don't wanna put them in
there.
1:15
And our models.py should really just be
models.
1:17
So let's add another new file.
1:19
That we will call forms.py.
1:21
Very creative, I know.
1:25
This is where we're gonna build the forms.
1:26
Now, our form that we're gonna build, I
wanna warn you.
1:27
There's a lot here.
1:31
So we're gonna do a lot of stuff.
1:32
So here we go.
1:34
Okay.
1:35
From flask_wtf import Form.
1:36
And what's kinda weird is that flask_wtf
doesn't use the flask.ext thing.
1:41
I really wish it did, but it doesn't.
1:45
And then we're gonna say from models
import User.
1:47
And let's start building our class.
1:51
So we'll make a RegisterForm.
1:56
You can call it registration form if you
wanted.
1:57
And it's going to be of the class form.
2:00
Form is the parent class.
2:02
So Username equals StringField.
2:04
And then this first argument that we give
here is the label.
2:09
So if you think about forms as they show
up on HTML, there's a label, right?
2:13
So we're gonna put the label of Username.
2:20
If we think about our Username field,
there's some things that have to be valid.
2:23
For it to be a real username, right?
2:27
So, there has to be data.
2:29
We probably want it to match a certain
pattern.
2:31
And we should probably make sure that it's
not already in the database.
2:33
Though, these requirements here, we call
these validators.
2:37
So we actually have an argument here,
called validators.
2:41
And we can put in validators.
2:45
So, I guess that we want there to be data,
right?
2:47
So DataRequired is one of the validators.
2:51
And then, the others we kinda have to
create our self.
2:55
Well, the regular expression we don't.
2:58
So, we want to match our patterns, so
we're gonna use Regexp,
2:59
which is a regular expression, regular
expression pattern.
3:03
So, what are we gonna give it.
3:07
Well, we have to give it a pattern.
3:09
What would our pattern be?
3:10
Well, we want it to start, and we only
want a through z lower case,
3:13
A through Z upper case, 0 through 9, and
then underscore.
3:18
And we want that to be as many of those
as, as they want, at least one, and
3:23
then we want that to end.
3:27
Why didn't I just use /w with a plus sign?
3:29
Since we're gonna be showing this in the
URL,
3:33
sometimes unit code doesn't play nicely
with the URL.
3:36
So I figure it's safer just to restrict
them to using ASCII than it is to
3:39
worry about something looking weird, or
not actually loading, or, or whatever.
3:43
If you wanna go with Unicode or
3:47
you wanna do some other requirement, then
that's fine, go for it.
3:49
But this is the one that I wanna do.
3:53
Okay.
So that's our pattern.
3:55
And I'm gonna pass in a message.
3:57
So, my message is going to be,
3:59
Username should be one word, letters,
4:02
numbers, and underscores only.
4:07
And look at that, I'm way out on column
95, so let's do.
4:11
I'm gonna make sure I can break this.
4:16
I'm gonna put a parenthesis here.
4:18
And I'll close the quote here.
4:20
Open a new quote.
4:22
Close that parenthesis.
4:24
This is a really handy way of not having
the light invalidate a line break.
4:25
Just a little, quick little tip here.
4:29
Okay, and then that closes our Regexp
thing.
4:32
Before we forget, let's go up here and
actually import those.
4:37
So, from wtforms import StringField,
4:40
because it's StringField that we're using.
4:43
And then from wtforms.validators
4:48
import DataRequired, and Regexp.
4:54
So far those are the only two we need.
4:59
All right.
5:01
As I said, we also wanted to make sure
that name did not already exist.
5:02
Right?
5:06
So we're gonna write our own validator
that we're gonna use here.
5:06
And we're gonna call this name_exists.
5:10
Then we close our list.
5:14
And we close our StringField.
5:16
So let's go right name_exists.
5:17
So here we go def name_exists, and this
takes two arguments,
5:20
it takes the form, that it's running on,
so in this case, register form,
5:24
and it takes the field that it's running
on, in this case username.
5:29
And we wanna do if User.select .where
User.name,
5:33
oops sorry we called it username, is equal
to field.data.exists.
5:39
So that just returns a Boolean of either
true or false.
5:47
This record's here or this record's not.
5:49
So, if that comes back as true, then we
want to raise ValidationError
5:51
of User with that name already exists.
5:57
But look, we imported something else and
we need to, or we're using something else,
6:03
we need to import that.
6:07
And we need to import ValidationError.
6:08
We've done a lot and we've only created
one field.
6:12
We need another blank line here too.
6:15
There we go.
6:17
Okay, so there's our username field.
6:18
Well, what comes next?
6:21
Well, in the register, we ask for a
username, we ask for an email address, and
6:22
we ask for a password twice.
6:26
So let's figure out how to do the email.
6:28
Email, and this is a StringField.
6:31
And we're gonna say Email, right?
6:36
Now what validators does email have?
6:40
It's got a few.
6:44
So the first one that it has, is that data
is required.
6:45
Something has to be there.
6:50
Second one is email.
6:52
It has to be email.
6:54
And then lastly, we're gonna make another
function named email_exists.
6:56
So let's go do those two.
7:01
So, first of all we need to import email.
7:03
And you know what?
7:08
That's 76 characters, I bet we're gonna
have to import something else, so
7:09
let's put a parenthesis there and
7:14
then that way we can import some stuff on
the next line too.
7:16
So name exists is already there, so let's
add def email_exists and
7:19
again, form, field, you know what?
7:25
Let's take this whole thing.
7:29
And paste it, because these two are gonna
be almost identical, except for
7:32
you want this to be email.
7:36
And you want this to be email.
7:37
So, same idea on these two, name exists,
email exists, except for
7:42
what they point to.
7:46
All right, so there's that one.
7:48
And let's add in password.
7:51
This is gonna be a password field.
7:52
Oh, new import.
7:55
And we'll say Password.
7:56
And it's gonna have some validators too.
7:58
Validators are gonna be DataRequired.
8:02
Cuz they have to give us a password.
8:05
Link and we'll say it has to be at least
two characters long for the password.
8:08
I would really say you make this like
seven or eight or something, but
8:12
it has to be at least two.
8:16
And then, we're gonna do a new one here.
8:17
EqualTo.
And
8:20
we're gonna say it's gonna be EqualTo
password2, whatever that is.
8:21
And then the message if it doesn't match
is gonna be, Passwords must match.
8:25
Okay.
8:32
Close our validators, close our password
field.
8:33
And let's actually go ahead and write
password2 before we go add our import.
8:35
So password2, hey there it is, is also a
PasswordField.
8:39
And it will say Confirm Password.
8:45
And really we could leave this one
completely alone, but
8:50
I do wanna add one validator to this of
DataRequired.
8:54
Just to make sure that they fill in some
data on that.
9:00
So we need to import PasswordField from
wtforms.
9:05
And we need to import Length and EqualTo
from our validator.
9:09
So StringField PasswordField.
9:15
And then here, we're gonna say Length and
EqualTo.
9:18
And that is our form.
9:23
That is a long, long form.
9:26
Wow!
9:29
There's a lot to building a form,
9:29
at least one that does all the stuff that
we want it to do.
9:31
Knowing how to customize validation will
go a long way when you start building your
9:34
own forms, though.
9:38
You need to sign up for Treehouse in order to download course files.
Sign up