WEBVTT

1
00:00:00.277 --> 00:00:04.585
In Flask and in Django, people often get
the wrong idea about forms.

2
00:00:04.585 --> 00:00:07.980
They hear the word form, and immediately
think about HTML forms.

3
00:00:07.980 --> 00:00:10.040
They think that forms are all about
display.

4
00:00:10.040 --> 00:00:12.830
And really that makes forms very limited.

5
00:00:12.830 --> 00:00:14.480
Forms are about validation.

6
00:00:14.480 --> 00:00:16.650
Making sure that your data matches a
certain pattern.

7
00:00:17.820 --> 00:00:20.450
The de facto form library for Flask is
Flask-WTF,

8
00:00:20.450 --> 00:00:24.620
and it builds on top of an older package
named WTForms, and

9
00:00:24.620 --> 00:00:27.040
we'll actually end up with both of them
installed.

10
00:00:27.040 --> 00:00:31.082
You'll install this with pip install
flask-wtf.

11
00:00:31.082 --> 00:00:37.190
This package also provides us with CSRF,
or cross-site request forgery, protection.

12
00:00:37.190 --> 00:00:39.110
What's cross-site request forgery?

13
00:00:39.110 --> 00:00:41.360
Well, imagine you've logged in to your
bank site and

14
00:00:41.360 --> 00:00:43.500
it keeps you logged in through a cookie.

15
00:00:43.500 --> 00:00:46.990
Now, imagine that some horrible person out
there sends you an image in an HTML page.

16
00:00:46.990 --> 00:00:52.920
But instead of an image URL, the URL goes
to some send me $1,000 page on your bank.

17
00:00:52.920 --> 00:00:55.770
Without CSRF your bank trusts that you
actually made that request, and

18
00:00:55.770 --> 00:00:57.730
goes ahead and sends them the 1,000 bucks.

19
00:00:58.865 --> 00:01:02.550
CSRF includes a custom one time code with
each submission.

20
00:01:02.550 --> 00:01:04.520
And, if the form doesn't have that code,
or

21
00:01:04.520 --> 00:01:07.260
doesn't have the right one, the request is
ignored.

22
00:01:07.260 --> 00:01:08.780
Okay, let's make our registration form.

23
00:01:10.220 --> 00:01:11.502
All right, so if we're gonna forms,

24
00:01:11.502 --> 00:01:15.530
our app.py, I can already tell it's gonna
get pretty crowded.

25
00:01:15.530 --> 00:01:17.080
So, we probably don't wanna put them in
there.

26
00:01:17.080 --> 00:01:19.188
And our models.py should really just be
models.

27
00:01:19.188 --> 00:01:21.585
So let's add another new file.

28
00:01:21.585 --> 00:01:25.192
That we will call forms.py.

29
00:01:25.192 --> 00:01:26.831
Very creative, I know.

30
00:01:26.831 --> 00:01:27.966
This is where we're gonna build the forms.

31
00:01:27.966 --> 00:01:31.640
Now, our form that we're gonna build, I
wanna warn you.

32
00:01:31.640 --> 00:01:32.850
There's a lot here.

33
00:01:32.850 --> 00:01:34.580
So we're gonna do a lot of stuff.

34
00:01:34.580 --> 00:01:35.550
So here we go.

35
00:01:35.550 --> 00:01:36.250
Okay.

36
00:01:36.250 --> 00:01:39.360
From flask_wtf import Form.

37
00:01:41.100 --> 00:01:45.760
And what's kinda weird is that flask_wtf
doesn't use the flask.ext thing.

38
00:01:45.760 --> 00:01:47.400
I really wish it did, but it doesn't.

39
00:01:47.400 --> 00:01:51.840
And then we're gonna say from models
import User.

40
00:01:51.840 --> 00:01:56.010
And let's start building our class.

41
00:01:56.010 --> 00:01:57.920
So we'll make a RegisterForm.

42
00:01:57.920 --> 00:02:00.420
You can call it registration form if you
wanted.

43
00:02:00.420 --> 00:02:02.932
And it's going to be of the class form.

44
00:02:02.932 --> 00:02:04.370
Form is the parent class.

45
00:02:04.370 --> 00:02:09.090
So Username equals StringField.

46
00:02:09.090 --> 00:02:13.990
And then this first argument that we give
here is the label.

47
00:02:13.990 --> 00:02:20.180
So if you think about forms as they show
up on HTML, there's a label, right?

48
00:02:20.180 --> 00:02:23.000
So we're gonna put the label of Username.

49
00:02:23.000 --> 00:02:27.980
If we think about our Username field,
there's some things that have to be valid.

50
00:02:27.980 --> 00:02:29.660
For it to be a real username, right?

51
00:02:29.660 --> 00:02:31.630
So, there has to be data.

52
00:02:31.630 --> 00:02:33.140
We probably want it to match a certain
pattern.

53
00:02:33.140 --> 00:02:37.420
And we should probably make sure that it's
not already in the database.

54
00:02:37.420 --> 00:02:41.070
Though, these requirements here, we call
these validators.

55
00:02:41.070 --> 00:02:45.490
So we actually have an argument here,
called validators.

56
00:02:45.490 --> 00:02:47.770
And we can put in validators.

57
00:02:47.770 --> 00:02:51.061
So, I guess that we want there to be data,
right?

58
00:02:51.061 --> 00:02:55.670
So DataRequired is one of the validators.

59
00:02:55.670 --> 00:02:58.190
And then, the others we kinda have to
create our self.

60
00:02:58.190 --> 00:02:59.380
Well, the regular expression we don't.

61
00:02:59.380 --> 00:03:03.469
So, we want to match our patterns, so
we're gonna use Regexp,

62
00:03:03.469 --> 00:03:07.503
which is a regular expression, regular
expression pattern.

63
00:03:07.503 --> 00:03:09.502
So, what are we gonna give it.

64
00:03:09.502 --> 00:03:10.740
Well, we have to give it a pattern.

65
00:03:10.740 --> 00:03:13.600
What would our pattern be?

66
00:03:13.600 --> 00:03:18.780
Well, we want it to start, and we only
want a through z lower case,

67
00:03:18.780 --> 00:03:23.270
A through Z upper case, 0 through 9, and
then underscore.

68
00:03:23.270 --> 00:03:27.870
And we want that to be as many of those
as, as they want, at least one, and

69
00:03:27.870 --> 00:03:29.610
then we want that to end.

70
00:03:29.610 --> 00:03:33.348
Why didn't I just use /w with a plus sign?

71
00:03:33.348 --> 00:03:36.110
Since we're gonna be showing this in the
URL,

72
00:03:36.110 --> 00:03:39.190
sometimes unit code doesn't play nicely
with the URL.

73
00:03:39.190 --> 00:03:43.900
So I figure it's safer just to restrict
them to using ASCII than it is to

74
00:03:43.900 --> 00:03:47.780
worry about something looking weird, or
not actually loading, or, or whatever.

75
00:03:47.780 --> 00:03:49.840
If you wanna go with Unicode or

76
00:03:49.840 --> 00:03:53.210
you wanna do some other requirement, then
that's fine, go for it.

77
00:03:53.210 --> 00:03:55.600
But this is the one that I wanna do.

78
00:03:55.600 --> 00:03:57.019
Okay.
So that's our pattern.

79
00:03:57.019 --> 00:03:59.258
And I'm gonna pass in a message.

80
00:03:59.258 --> 00:04:02.954
So, my message is going to be,

81
00:04:02.954 --> 00:04:07.791
Username should be one word, letters,

82
00:04:07.791 --> 00:04:11.934
numbers, and underscores only.

83
00:04:11.934 --> 00:04:16.769
And look at that, I'm way out on column
95, so let's do.

84
00:04:16.769 --> 00:04:18.100
I'm gonna make sure I can break this.

85
00:04:18.100 --> 00:04:19.320
I'm gonna put a parenthesis here.

86
00:04:20.460 --> 00:04:22.640
And I'll close the quote here.

87
00:04:22.640 --> 00:04:23.340
Open a new quote.

88
00:04:24.430 --> 00:04:25.550
Close that parenthesis.

89
00:04:25.550 --> 00:04:29.390
This is a really handy way of not having
to like, invalidate a line break.

90
00:04:29.390 --> 00:04:32.180
Just a little, quick little tip here.

91
00:04:32.180 --> 00:04:37.080
Okay, and then that closes our Regexp
thing.

92
00:04:37.080 --> 00:04:40.182
Before we forget, let's go up here and
actually import those.

93
00:04:40.182 --> 00:04:43.999
So, from wtforms import StringField,

94
00:04:43.999 --> 00:04:48.501
because it's StringField that we're using.

95
00:04:48.501 --> 00:04:54.091
And then from wtforms.validators

96
00:04:54.091 --> 00:04:59.870
import DataRequired, and Regexp.

97
00:04:59.870 --> 00:05:01.698
So far those are the only two we need.

98
00:05:01.698 --> 00:05:02.580
All right.

99
00:05:02.580 --> 00:05:06.150
As I said, we also wanted to make sure
that name did not already exist.

100
00:05:06.150 --> 00:05:06.920
Right?

101
00:05:06.920 --> 00:05:10.980
So we're gonna write our own validator
that we're gonna use here.

102
00:05:10.980 --> 00:05:14.220
And we're gonna call this name_exists.

103
00:05:14.220 --> 00:05:16.320
Then we close our list.

104
00:05:16.320 --> 00:05:17.934
And we close our StringField.

105
00:05:17.934 --> 00:05:20.635
So let's go right name_exists.

106
00:05:20.635 --> 00:05:24.642
So here we go def name_exists, and this
takes two arguments,

107
00:05:24.642 --> 00:05:29.477
it takes the form, that it's running on,
so in this case, register form,

108
00:05:29.477 --> 00:05:33.881
and it takes the field that it's running
on, in this case username.

109
00:05:33.881 --> 00:05:39.580
And we wanna do if User.select .where
User.name,

110
00:05:39.580 --> 00:05:47.062
oops sorry we called it username, is equal
to field.data.exists().

111
00:05:47.062 --> 00:05:49.710
So that just returns a Boolean of either
true or false.

112
00:05:49.710 --> 00:05:51.810
This record's here or this record's not.

113
00:05:51.810 --> 00:05:56.430
So, if that comes back as true, then we
want to raise ValidationError

114
00:05:57.560 --> 00:06:02.170
of User with that name already exists.

115
00:06:03.810 --> 00:06:07.505
But look, we imported something else and
we need to, or we're using something else,

116
00:06:07.505 --> 00:06:08.655
we need to import that.

117
00:06:08.655 --> 00:06:10.545
And we need to import ValidationError.

118
00:06:12.985 --> 00:06:15.815
We've done a lot and we've only created
one field.

119
00:06:15.815 --> 00:06:17.865
We need another blank line here too.

120
00:06:17.865 --> 00:06:18.945
There we go.

121
00:06:18.945 --> 00:06:20.845
Okay, so there's our username field.

122
00:06:21.845 --> 00:06:22.645
Well, what comes next?

123
00:06:22.645 --> 00:06:26.755
Well, in the register, we ask for a
username, we ask for an email address, and

124
00:06:26.755 --> 00:06:28.940
we ask for a password twice.

125
00:06:28.940 --> 00:06:31.690
So let's figure out how to do the email.

126
00:06:31.690 --> 00:06:35.190
Email, and this is a StringField.

127
00:06:36.860 --> 00:06:40.810
And we're gonna say Email, right?

128
00:06:40.810 --> 00:06:42.330
Now what validators does email have?

129
00:06:44.510 --> 00:06:45.560
It's got a few.

130
00:06:45.560 --> 00:06:49.740
So the first one that it has, is that data
is required.

131
00:06:50.790 --> 00:06:52.410
Something has to be there.

132
00:06:52.410 --> 00:06:54.470
Second one is email.

133
00:06:54.470 --> 00:06:56.070
It has to be email.

134
00:06:56.070 --> 00:06:59.725
And then lastly, we're gonna make another
function named email_exists.

135
00:07:01.750 --> 00:07:03.420
So let's go do those two.

136
00:07:03.420 --> 00:07:08.837
So, first of all we need to import email.

137
00:07:08.837 --> 00:07:09.475
And you know what?

138
00:07:09.475 --> 00:07:14.200
That's 76 characters, I bet we're gonna
have to import something else, so

139
00:07:14.200 --> 00:07:16.244
let's put a parenthesis there and

140
00:07:16.244 --> 00:07:19.717
then that way we can import some stuff on
the next line too.

141
00:07:19.717 --> 00:07:25.880
So name exists is already there, so let's
add def email_exists and

142
00:07:25.880 --> 00:07:29.394
again, form, field, you know what?

143
00:07:29.394 --> 00:07:32.832
Let's take this whole thing.

144
00:07:32.832 --> 00:07:36.431
And paste it, because these two are gonna
be almost identical, except for

145
00:07:36.431 --> 00:07:37.698
you want this to be email.

146
00:07:37.698 --> 00:07:39.120
And you want this to be email.

147
00:07:42.140 --> 00:07:46.840
So, same idea on these two, name exists,
email exists, except for

148
00:07:46.840 --> 00:07:48.360
what they point to.

149
00:07:48.360 --> 00:07:51.300
All right, so there's that one.

150
00:07:51.300 --> 00:07:52.970
And let's add in password.

151
00:07:52.970 --> 00:07:55.350
This is gonna be a password field.

152
00:07:55.350 --> 00:07:56.190
Oh, new import.

153
00:07:56.190 --> 00:07:58.990
And we'll say Password.

154
00:07:58.990 --> 00:08:00.700
And it's gonna have some validators too.

155
00:08:02.330 --> 00:08:04.925
Validators are gonna be DataRequired.

156
00:08:05.990 --> 00:08:07.190
Cuz they have to give us a password.

157
00:08:08.320 --> 00:08:12.520
Link and we'll say it has to be at least
two characters long for the password.

158
00:08:12.520 --> 00:08:16.040
I would really say you make this like
seven or eight or something, but

159
00:08:16.040 --> 00:08:17.530
it has to be at least two.

160
00:08:17.530 --> 00:08:20.200
And then, we're gonna do a new one here.

161
00:08:20.200 --> 00:08:21.330
EqualTo.
And

162
00:08:21.330 --> 00:08:25.230
we're gonna say it's gonna be EqualTo
password2, whatever that is.

163
00:08:25.230 --> 00:08:30.470
And then the message if it doesn't match
is gonna be, Passwords must match.

164
00:08:32.260 --> 00:08:33.340
Okay.

165
00:08:33.340 --> 00:08:35.550
Close our validators, close our password
field.

166
00:08:35.550 --> 00:08:39.490
And let's actually go ahead and write
password2 before we go add our import.

167
00:08:39.490 --> 00:08:44.355
So password2, hey there it is, is also a
PasswordField.

168
00:08:45.630 --> 00:08:49.540
And it will say Confirm Password.

169
00:08:50.610 --> 00:08:54.498
And really we could leave this one
completely alone, but

170
00:08:54.498 --> 00:08:58.150
I do wanna add one validator to this of
DataRequired.

171
00:09:00.180 --> 00:09:03.840
Just to make sure that they fill in some
data on that.

172
00:09:05.280 --> 00:09:09.640
So we need to import PasswordField from
wtforms.

173
00:09:09.640 --> 00:09:13.810
And we need to import Length and EqualTo
from our validator.

174
00:09:15.440 --> 00:09:18.470
So StringField PasswordField.

175
00:09:18.470 --> 00:09:22.020
And then here, we're gonna say Length and
EqualTo.

176
00:09:23.500 --> 00:09:25.020
And that is our form.

177
00:09:26.070 --> 00:09:29.212
That is a long, long form.

178
00:09:29.212 --> 00:09:29.842
Wow!

179
00:09:29.842 --> 00:09:31.545
There's a lot to building a form,

180
00:09:31.545 --> 00:09:34.318
at least one that does all the stuff that
we want it to do.

181
00:09:34.318 --> 00:09:38.305
Knowing how to customize validation will
go a long way when you start building your

182
00:09:38.305 --> 00:09:39.359
own forms, though.
