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.
Related Discussions
Have questions about this video? Start a discussion with the community and Treehouse staff.
Sign upRelated Discussions
Have questions about this video? Start a discussion with the community and Treehouse staff.
Sign up
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
to like, 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 upYou need to sign up for Treehouse in order to set up Workspace
Sign up