Flask-WTF Forms9:39 with Kenneth Love
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