Full Web Apps with FastAPI Transcripts
Chapter: Users and HTML forms
Lecture: Receiving registration data with view model

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

Talk Python's Mastodon Michael Kennedy's Mastodon