Building data-driven web apps with Pyramid and SQLAlchemy Transcripts
Chapter: Client and server-side validation
Lecture: Server-side data exchange
0:00 I feel like we kind of made a mess
0:01 and we actually skipped over some important stuff
0:03 in the previous chapter when we talked about forms
0:06 and user input.
0:08 We didn't do all the validation that we should have
0:11 and, we kind of made a mess while doing it only partially.
0:14 So we're going to do some dramatic clean up
0:16 using a design patern called view models.
0:20 Let's just jump right into it and see it in action
0:22 and then we'll come back and review the concepts.
0:25 We're on to a new chapter so that means on to a new section
0:28 in our GitHub repository. So Chapter 13 validation final
0:33 of course starter is the same
0:34 but right now, but final is going to be
0:36 whatever we do in this chapter.
0:38 These numbers are of course subject to potentially changing
0:41 as we evolve the course, but for now Chapter 13.
0:45 I've already setup all the virtual environments
0:46 and stuff for here, let's open it up and get going.
0:50 So let's start by looking over in the AccountController.
0:53 That's probably the dirtiest or messiest
0:57 on this little auto import stuff up here.
1:01 I kind of got stuff sorted out of order, but here we go.
1:03 So even when we are just trying to show the homepage
1:07 we do have to pass the user but we also have to pass this user ID.
1:09 Remember we have to constantly get that from the cookie_auth
1:15 here to pull it back, but even down here we're passing it.
1:18 We're going to pass all this extra data
1:21 when we're even just showing the form.
1:23 So what we're going to do is we're going to create
1:26 these things called view models.
1:27 See, they're just classes, and the classes are
1:29 deeply tied to the data that's exchanged by this method
1:34 and the data contained in the HTML.
1:37 So we know there is a form and register
1:40 that is going to exchange that data.
1:43 And we know the layout, the shared layout, needs that data.
1:46 So what we're going to do
1:47 is we're going to bundle that up into a class.
1:50 So we sort of keep the class in sync with the data
1:53 in the validation and all that checking stuff
1:55 is going to go into that class as well.
2:00 There are a lot of these things and they're
2:02 like I say very much tied to
2:04 the things being exchanged here.
2:07 So just like we did with templates we created
2:10 some for account, we created some for the home and so on.
2:13 We're going to do the same thing for view models.
2:15 It's going to have a similar
2:16 although, not exactly identical, structure.
2:19 So let's come over here and create
2:21 a sub-package called view_models
2:24 and in here I'm going to create a directory called shared
2:30 and let's do one for account.
2:33 So as you might guess from these names
2:35 there's going to be a base class that has
2:36 lots of stuff that is shared like almost
2:38 everything needs this error
2:40 and absolutely everything needs user_id.
2:44 So that kind of stuff is going to go in there
2:46 as well as the ability to take an arbitrary
2:49 in memory class and convert it to a dictionary
2:51 which is what's required in the web framework.
2:54 So let's start here.
3:00 Create a thing called view_modelbase
3:02 and we'll create a class called ViewModelBase
3:04 and this is going to be the base class of all of the
3:06 other view models.
3:07 And it's going to have two important things.
3:10 It's going to come in here and it's going to take the request
3:13 and of course, let's go ahead and type that
3:15 so things work a little nicer
3:17 and it's going to come here and it's going to say
3:18 sel.request. Just go ahead and hang onto this
3:22 because sometimes the derived classes will need it.
3:25 And, let's go ahead and set that error
3:30 which is a str to None.
3:33 So this means any of the forums that have errors
3:35 it's just it's not everything using this
3:37 but it's so super common
3:38 that we're going to just put it here.
3:40 We'll say self.user_id.
3:44 And this is an int.
3:46 And for a second I'll say None.
3:47 Alright, so these shared pieces of data
3:50 are going to be now available everywhere.
3:53 Well that's the storing the common data
3:57 and how do we return this to a dictionary?
3:59 Well let's just say create a function called to_dict
4:04 and it's just going to take
4:05 the attributes or fields here that we're setting
4:08 which actually go into
4:10 an internal dictionary for this class.
4:12 I'm just going to return those. Say self__dict__.
4:15 Alright, we can override this in base classes.
4:18 For more complicated things, but, here we are.
4:21 So let's actually do a few more things.
4:23 Here, we'll say self.equestdict, remember this thing?
4:27 It's going to be requestdict, going to import that
4:30 .create_from_request. Let's go actually go to our cookie_auth
4:39 and we're going to say, try to get the user ID from the cookie.
4:44 Maybe it doesn't exist, maybe it's messed with
4:46 might be None, but if it's not, let's store it.
4:50 Okay, so this is the shared bits.
4:53 And everything is going to derive from that.
4:57 Let's go over here and create an AccountHomeViewModel.
5:06 So AccountHomeViewModel, it derives from this, whoops.
5:10 No, it derives from this, which we have to import.
5:14 Takes an init, it has a request, which is a request.
5:20 And, we have to make sure that we call super()
5:23 init with the request before we do anything else.
5:27 Now, let's go back here on this home thing
5:30 on the details let's do this one first.
5:33 What other information do we have to do?
5:36 Well, we're going to actually pass the user along as well.
5:39 Alright, we're actually getting the user and we're
5:41 trying to show it, so the user is included.
5:44 Let's roll that in here.
5:46 After this, we'll say self.user
5:49 maybe with a little more space.
5:50 self.user = UserService.find_user_by_id(self.user_id)
6:02 Look how slick that is.
6:03 Okay, and let's just make sure this can take None.
6:06 Yeah, if it's none it will just
6:08 come back with nothing, great.
6:10 So, let's go re-envision this method
6:13 now much cleaner with view models.
6:14 So we'll say vm = AccountHomeViewModel()
6:21 we're going to pass a request
6:23 we don't need this stuff, we just say
6:24 look, if there's no vm.user
6:28 we got to get out of here.
6:29 Otherwise, or turn vm.to_dict().
6:33 Now, this one didn't get that cleaned up
6:35 but the more validation, and the more complicated
6:38 the data exchange, the better and better this gets.
6:41 Let's just see that our account page works.
6:44 Let's go over here, we may have to log in, nope.
6:47 Click on our account page, oh look at that.
6:49 Account is here, whether or not we're logged in is here
6:51 the user was found, all of that, completely isolated.
6:55 So what we get is these view methods that our super
6:59 focused on orchestrating what has to happen.
7:02 They're not down in the nitty gritty details.
7:04 So, that one was pretty nice.
7:06 Let's do one about register.
7:11 We're going to create a couple of these
7:13 so I think what I'll do is I'll just
7:15 talk through this one more time
7:16 and then we'll go quick for the rest.
7:20 All of them are ViewModelBase derive.
7:23 They all have an init.
7:26 That takes a request.
7:30 They all call super, like that.
7:33 Thank you PyCharm, and then they add additional data.
7:36 So let's go look over here, and copy this to get started.
7:41 Let's go over here and see what we need.
7:42 Well, we need, let's go like this.
7:49 Import that real quick and we're going to say
7:51 return vm.to_dict().
7:52 What else do we need?
7:53 We have user_id, and we have error, common.
7:56 But these three things have to come along.
7:58 So let's just cut them to sort of
8:00 remind us what we got to put here.
8:02 So we're going to have self.email = None
8:12 So, here's those three pieces of data
8:13 and now, that comes super nice and clean.