Full Web Apps with FastAPI Transcripts
Chapter: Users and HTML forms
Lecture: Receiving registration data with view model
0:00 So we've got the GET > POST part of our GET > POST > Redirect pattern in place,
0:04 and we no longer need those little prints to see it in action.
0:07 Now, there is another thing that we need to do here.
0:11 We need to work with the data that's being passed back into this form here.
0:15 So what's happening in this part is we're creating the blank data to pass along to
0:20 just create the empty field. But we need to accept the data that's being passed
0:24 over to it from this template through FastAPI.
0:29 So give it something, say load from this form.
0:32 In fact, we don't have to even pass this over,
0:34 we're gonna be able to get this from the request itself.
0:37 So let's go ahead and add this function. Now,
0:39 why is there this separate thing?
0:42 Why is this extra one? Because if we just grab the form right here and
0:45 and check, it's going to be empty for the name in the GET version,
0:49 but it's not gonna be empty in the POST version.
0:51 It needs to be empty in the GET, it cannot be empty in the POST,
0:55 right? So we wanna have different behavior and this, this allows us to say,
0:58 like, OK, here's the kind of get it loaded up,
1:00 GET version. And here's what we do in the POST.
1:03 So this is gonna be interesting.
1:05 What we need to do is going to say the form which is gonna effectively be
1:08 like a dictionary is gonna go to the request,
1:11 and we just say form() like this. Seems good,
1:14 right? But if we say form dot notice the stuff that PyCharm is showing
1:18 us coroutine, coroutine, coroutine, coroutine, awaitable,
1:23 awaitable object. So what this is, when we do this form read here,
1:28 there's a couple of things that are happening,
1:30 and one is that this is an asynchronous read.
1:33 It could be some kind of blocking thing,
1:35 like maybe it requires that it's submitting like an image as part of the form POST,
1:39 a multipart form POST or something.
1:41 It's, it's reading the stream, so we want to make sure we don't block during
1:44 that. So in order to do that,
1:46 we're going to get to one of the very exciting features,
1:48 our first chance to touch it,
1:49 and we're going to dive deep into this on the database side,
1:51 it's we're gonna use async and await.
1:53 So to convert this task, this potential result to an actual thing,
1:59 but allow other work to happen at the same time.
2:01 We have to say, await that.
2:03 And now if we go to form,
2:04 you can see we have a multi dict form data type of object with keys and
2:08 items and dictionary stuff. But in order to use await,
2:11 we have to have an asynchronous function up here, and in order to do anything useful with
2:17 our asynchronous function. Maybe if I left it like this, what you would see, you
2:21 would see that there's a there would be something like, this was not awaited, or
2:25 something like that at the end of your code.
2:27 If you see an error like that,
2:28 that means you forgot an "await" somewhere,
2:31 and we need it there. And again,
2:32 in order to call this here,
2:34 we need to make this in an async function.
2:36 And here's where it gets awesome,
2:37 FastAPI automatically supports both synchronous and asynchronous view methods.
2:43 So this is no problem. We don't have to do anything else.
2:46 We don't have to choose a different server or a different way to run FastAPI.
2:49 There's one reason, Uvicorn, as the primary server for it,
2:53 all of this stuff just automatically adapts.
2:55 But once we're down here and we start down this path,
2:59 we've got to cascade the asyncs back up.
3:02 OK, so we've got that done.
3:03 Let's go ahead and get these things,
3:10 the password and the email. The other thing we need to do here,
3:16 though, is we need to check,
3:17 we need to check that these values are set properly,
3:20 so we'll say, if not self.name.
3:22 Now we could put validation, and we will put validation in the form that says
3:26 the name is required, but there's nothing that forces the browser to follow it. If
3:32 you're using it normally, of course,
3:33 Chrome or Firefox will say this form field is required.
3:37 But if, what if you just grab something like requests and do a POST against this
3:41 to see what's gonna happen? Well,
3:43 none of that validation is gonna happen,
3:44 so we have to make sure we re do that here.
3:46 So if name, there's no name or not
3:49 self.name.strip() like a space or tab won't do it.
3:53 I'm gonna say self.error equals the name, your name.
3:59 And we're going to do something like very,
4:01 very similar to this for the others. That did not work out as smooth as I hope
4:06 it did. There we go. elif email, your email is required. And then we could do password.
4:12 Let's change this a little bit,
4:14 say, or the length of the password is too short.
4:19 Fewer than five characters say: "Your password is required and must be at least 5 characters"
4:26 I think we might be ready.
4:29 This is the processing and validation side of our form,
4:33 and we could do could, we could grab that age and say we want to convert that to
4:38 a integer, and if it's not an integer,
4:39 it has to be an integer.
4:40 We'll see some other stuff that we could do around that as well.
4:42 But I'm just gonna leave it like this for now.
4:45 So now the next question is,
4:46 did that work? We'll say, if, how do we know?
4:49 Well, one way is that there is now an error contained in the view model
4:52 We could also have this return, you know, return False right if we want.
4:57 But I'll just check whether or not there's an error.
4:58 So if there is an error,
4:59 we want to do exactly this right here.
5:03 Otherwise I'll just do a print really quick.
5:06 "TODO: Redirect" because this is, it takes a little bit of juggling here.
5:09 So we're gonna do that in the next video, and I'll just print this out,
5:15 like so. Just return it back again one more time and we'll change this last line.
5:19 So what we're gonna do is, it's gonna come in,
5:20 submit the data. We're gonna load it up,
5:22 grab the form asynchronously read it, validate it,
5:25 store the data. If things are
5:27 not good, instead of just returning,
5:29 we want to return the data they submitted back to them to reload that form with
5:34 what they've typed. So if you say your email is wrong,
5:37 we'll leave the one they typed wrongly there so they can make adjustments to it rather
5:41 than make them fill out the form from scratch.
5:43 Give this a shot, see what we get.
5:45 So if we come over here and we click on register and if I just submit
5:49 nothing, I'm gonna put something there really quick. Submit nothing.
5:53 Oh, something has gone wrong here.
5:54 What has happened? Yes, I meant to mention this earlier.
5:57 When you work with forms, just like when you work with templates or you work with
6:02 static files, there's other dependencies you need.
6:05 And when we work with forms,
6:06 this python-multipart package is required.
6:09 Well, let's go install that.
6:10 Then I think our form will actually be ready to go.
6:13 Install this, there we go. Give it one more try,
6:15 resubmit it. Look at that,
6:17 "Your name is required". How awesome is that?
6:19 So now if I put my name and I try to submit it,
6:23 "Your email is required". OK, put my email, password,
6:27 now great, let's put the letter "a". Nope,
6:30 must be 5, 5 "a". 1, 2, 3, 4, 5. All right,
6:34 here we go. It worked,
6:36 the only one way we know that worked is one,
6:38 we saw the "TODO: Redirect" here.
6:40 The other is the error message went away.
6:42 Super cool, right? So if we didn't resubmit that dictionary right here, every time
6:49 the form would empty itself.
6:50 But we're reloading the form every time with whatever they passed in.
6:54 So in case this was missing, it's going to say, it keeps the Michael and the password
6:58 and it just says, just put your pass, your email back.
7:00 Really, really sweet way to do this.
7:02 GET > POST, almost redirect pattern.
7:05 Virtual redirect. Here, looks like we got it working.