Full Web Apps with FastAPI Transcripts
Chapter: View models and services
Lecture: Adding a view model to the home/index view
0:00 Welcome to chapter five. Let's get started working on these two patterns view models and services So, as you can see here,
0:07 I've made a copy of what we completed in chapter four and that's what we're starting with in chapter five.
0:12 Let's go ahead and open it up and over here we're gonna focus on this view right here. Let's just run it and remind ourselves what it looks like.
0:21 So it's this page here that shows a couple of things, and we're gonna add something else that's gonna be interesting that we weren't able to do
0:28 easily before. Here, we've got the number of projects, the number of releases, the number of users and then the most recent projects.
0:39 So what we're gonna do is we're gonna create a view model that provides this page, this template, that data.
0:46 Now we're gonna start simple here because we're not exchanging data. We're not accepting data yet. Later,
0:52 we're going to write a login and a register bit of functionality. This is gonna let us do many things,
0:57 work with HTML forms and validation and all that. That's when you'll see these concepts of view models really shine. But I wanna, you know,
1:04 make progress down that road until we can get to the user section in the coming
1:08 chapters. What we're gonna do is we're gonna create a view model that just provides
1:11 data to this page, and we're gonna have some services that work with projects and users. Let's go and create a "service"s folder.
1:21 And like I said, we're probably gonna have to. I'll go ahead and just write it
1:24 here, package_service. Again, this is not web service in the traditional sense of
1:30 the API. This is a thing that bundles functionality and think of it as a layer between your actual data layer and the rest of your application.
1:38 It knows how to do all the queries to SQLAlchemy. It knows how to interact with external APIs that you might need for,
1:45 say, sendgrid or mailchimp or something like that, using either of those services. While the user service,
1:50 here, the user service might know how to verify passwords with cryptography. In addition, it's not just data access layer,
1:57 that's no what. It's why I'm not calling it that. OK, so we're gonna need a couple of things in order to do that, uh, to make these useful.
2:04 But let's go ahead and do our view models, now recall that we have some really cool naming conventions that allow me to say, well, I'm looking at this.
2:13 Let's say I'm looking at this function, this view method. I want to know where's the template. I don't have to guess what it's called or like
2:22 track it down. I go. OK, so it's home index. So it's home index. We're gonna follow exactly the same pattern for view models.
2:30 That way, if you're in the view model, you know where the function is. That is the view function. You know where where the template is.
2:37 It's really nice and organized. So I'm gonna go, create a directory called viewmodels. And in there we're gonna make a directory called
2:45 home, and at home, we're really just gonna probably need one view model. We're also gonna need to have a common base class.
2:53 I'm gonna create something called shared, and I'll put, I'm gonna calle it viewmodel_base in here.
3:02 Maybe just a few models. Here we go, and we'll have a class. Now, it might seem silly like, why do I need this base class? What is its job to be?
3:10 There's a couple of things that we want to make sure every single page has all the time. For example, when we do user management stuff,
3:18 we wanna have it be the job of these view models to tell you what user is logged in and potentially provide you with that user and
3:25 so on. If you're in a form, they always are gonna be able to provide an error message if something goes wrong with
3:30 the form. So there's a couple of things like that that are gonna be part of this base model. The other one is, we need ways to easily provide the
3:37 fields of the class as a dictionary. So those are the two rules here. So a ViewModelBase, let's call it. And down here its gonna have a
3:48 couple of things so we'll have an error, which is an Optional, we're gonna need some typing here, string, which is None. And let's have ah user_id,
3:59 which is an Optional integer, which is also None. So those were the two core shared pieces of data. We're probably gonna pass around some more.
4:08 It's often helpful to have access to the request that comes in as you'll see for, like, cookie management and stuff like that. So let's go and add that
4:18 in here as well, and PyCharm will gladly add that. Now wouldn't it be cool if it typed that for me?
4:25 OK, so it'll have this stuff and then, in order to pass these things back we're just gonna take advantage of the fact that when you do this,
4:32 what it's really doing is putting an entry in the class dictionary. So we'll say def to_dict, gonna return a dictionary. And what's gonna be in here,
4:45 we're gonna do is just return self.__dict__. So this is looking good. This is gonna be something common to all of our
4:52 view models. We're probably never going to touch it again. And then, now let's go. Remember, we are in the index, and in the home
5:01 I'll say, indexviewmodel. Add a class IndexViewModel like that. I'ts donna derive from our base class, which we import. Thank you,
5:13 PyCharm. And it's going to need, yes, it'll need a constructor. Alright
5:18 And PyCharm says there's one that requires something passed in so well let PyCharm write all that code for us. Thank you, thank you. Super cool.
5:27 So let's go back. Now that we've got our structure in place, Let's go back up here to our home view and say,
5:32 What were we doing before? we were coming up with all these elements,
5:37 this dictionary that has a package_count, a release_count, user_count and a list of packages.
5:41 I'm gonna copy this over. And this will give us a hint on what we're going to need in our indexviewmodel. Over here tells you that we're probably gonna
5:49 need a self.package_count, which is an integer, and I'm just gonna put None, I'll put zero for a minute.
5:55 We're gonna come up with a better answer in a moment, and we need a release, release_count and a user_count.
6:06 And then it is the order here we're gonna need packages. Which is gonna be a list of something.
6:18 We don't know what goes in the list yet because we don't have a class for it, we will in a minute. And we'll just say like that.
6:23 OK, so let me go and comment this out and see if we can now swap this out to be our view model. And I'll just put 1, 2 and 3.
6:34 So we have some little big data, it's just so you can tell actually, something is flowing from one place to the other.
6:40 So instead of this. What we're gonna do is we're gonna say vm for view model. It's one of these. We've gotta,
6:47 of course, import that as above. And then we have to pass in the request. PyCharm'll help us get that close.
6:56 close. We've gotta type that in. FastAPI uses the type hint here to know that what you want to pass in is the request. So this is all we have to do,
7:08 and then it'll start passing it over. And then again, if we don't pass anything,
7:12 watch what happens. We get a crash and the crash is NameError somewhere. The NameError is right here.
7:21 package_count. You're supposed to give me a package_count, and you didn't. So let's provide the data from here,
7:26 which will be to_dict. It was basically like before. It's gonna build up that dictionary like we saw. Not as much data, but close.
7:35 Yes! Look at that 3, 1, 2. Maybe we could've changed the order to go 1, 2, 3 but still cool, right?
7:39 So it's provided this information. It's gonna get it from somewhere else in a minute, but it's provided this information,
7:45 and it's given us an empty list of new releases. We're gonna need to get them from somewhere again.
7:50 Pretty awesome, right? So this is how we're going to pass data over to our template. Now let's go back and look real quick.
7:58 Suppose that we want to make sure that the request is not None. We want to make sure that you know some data that we're pulling back is valid
8:07 here or if we pass in more things like we're passing a form that the form contains the elements that we expect.
8:13 It's really, really easy to write a unit test against that class right there and do that validation without doing any web test infrastructure,
8:22 which is always kind of a pain around unit testing web applications.
8:25 So it allows you to really carefully test the exchange between the template and this thing
8:29 You could even actually use the template engine to directly try to render a test instance of one of these things.
8:36 So that's pretty cool. It's also going to keep this method extremely simple. For real
8:41 ones, it's gonna be slightly more complicated than that but not a lot.