RESTful and HTTP APIs in Pyramid Transcripts
Chapter: Validation with view models
Lecture: Moving validation to view models
0:01 Now let's return to our create auto, and I've added just a few little tweaks to it
0:06 down here I realized I had forgot to actually return the error and say bad response, so I am now submitting the errors back to the user
0:13 if they somehow do something wrong, and it turns out I also need to call to dick here. Okay, so this is all looking pretty good, like this works,
0:22 let's just run it and verify so if I came over here, I can get all the autos, and you can see here's the format we're looking for
0:28 and let's go and create one, so we are going to do a post here that's the create auto function we're working on
0:35 and let's set this to the year 2020 and we'll call this the Opel concept 2020 and here's all the details about it, right.
0:45 Now, notice over here, first of all let's just make this invalid, like if it's just like this, right that's invalid json so this should not submit
0:52 we can come down here and see could not parse your post invalid character at such and such place, ok
0:58 it looks like I'm missing like a little space there but that's fine if I take one of these lines out, like let's just take this price out
1:05 I'll call it x price because it's going to be looking for price and we go over here and we'll see in the validation
1:10 it's looking for the price, actually I think it might not work so well let me take out the damage, okay, we'll try this
1:19 notice we get a 400 bad request, unexpected keyword argument x yeah okay, try again, could not parse positional thing require, right
1:31 so we're not really making it past this, let me do one more and let me just put damage is none, null in this case
1:39 it turns out none on the other side, and I guess damage was okay to be none, but this is not, alright, so 400 bad request,
1:47 there are errors with your submitted car, brand is a required field, so this is some of our validation, like this is that part right there
1:54 and let's just see that some other stuff is working so if I make price like negative, I'll have two errors
2:00 brand is required, price must be non negative, you can give it away but you can't make people pay you you can't pay people to be buyers.
2:10 Alright, so this is working pretty well, and like I said this is good, there's no problem in functionality here
2:17 the problem is like this is really what we're trying to do here and it's totally obscured and it's hard to test
2:23 so let's move this to a view model concept, so I want to come over here, and again like all well factored sites
2:30 while this broken into different pieces, so view models so we know right where to look when we want to find a particular view model here,
2:38 I'm going to start out with just a flat class, then we'll do a little work on this so I'll call this create auto view model, like so,
2:45 and in here I want to create a class, create auto view model, I think that's solid, and let's go and import this,
2:54 so what we're going to do up here, I want to create import one of these I want to create it, and I'll call it vm for view model
3:01 and now I want to give this a function so this part about here this car from dictionary bit, not a big fan of that,
3:08 let's get this down to just parsing the json body; now, I would actually push this off to the view model itself
3:16 but it's too complicated to pass the string of just the body because that's bytes encoded so I also have to pass the request and coding type
3:26 and it's not worth to try to decouple this in pyramid. So we're going to leave this part here, but we're going to take this dictionary
3:33 and we want to go and pass that over here, so car data like that, now we're going to say I could have it actually do all the work right there
3:44 that's totally reasonable, I'm going to make it a little bit explicit about when this gets processed, so I am going to say compute details
3:51 and so this will compute the details, the validation, things like that and then we'll just say something to this effect, if vm.errors
4:00 so it's going to have an errors property, and if it does you can bet that it's going to look like this, all right
4:07 so if there is an error, vm.errors, say there were errors like this now actually, this construction of the error message here
4:15 let's change that as well, let's go over here and just say this is going to be vm.error message like so.
4:24 I'll leave this here for just a moment, because we'll just copy that over. Okay, so you can see PyCharm is saying
4:29 you are passing something where it doesn't belong, this function doesn't exist, this field doesn't exist, this property doesn't exist
4:35 so let's start to change this, let's go over and say it's going to have init that takes a data dictionary and let's just store it for now.
4:47 It's all going to need the self.errors, which is going to be empty list,
4:51 and let's give it an error message as well, I call it error message, like that. We'll make that a property, not with a little complete thing
5:05 and let's go ahead and just copy this bit over here, like so, not quite like that.
5:12 And we'll just return message, okay so we're just going to move that over and get it out of the way, get the computation and work on that
5:20 we'll just say there's an error message and it can talk about. The next thing that we want to do is we want to work with this method
5:27 which doesn't exist but we're going to add it so it's going to do some work one of the things that we need to capture here is the car,
5:34 so we're going to parse out the car from the data dictionary, the other thing is we're going to do this validation
5:41 so let's take this, we're going to move that down and this I am just going to delete,
5:46 okay so notice here's vm.car use that car there, we're going to pass that along and let's go back down here and put all of this validation
5:56 so I'll say to do parse car and we'll do all this validation stuff and here this will be self.errors, and we're going to work with that car in a minute
6:08 but just to replace this variable here, okay so it looks like that's working, now the other thing we have to do is actually create the car
6:18 so just to get it to work really quick, I am going to say car= car.from dict,
6:22 now we're going to improve upon this in a second, but let's go and import that, okay so this is looking good, let's go ahead and run it,
6:30 and see if it still works the same, let's do one more quick double check over here
6:35 do a little clean up, so get the body, we're now creating the view model and now this is the fixed guaranteed validation that we're doing
6:45 the parsing and validation, we basically promised it will never get more complicated than this, other than possibly passing in also a car id
6:53 which we would have to get from somewhere, so maybe a little more arguments to the initializer,
6:58 but other than that, like we can have as much validation as we want do you want to pass in different types of data and unit test it,
7:04 well this is what goes in your test and then you look in errors, super nice.
7:11 So let's go and run this and see how it works, notice that this better be vm.car, alright, it looks like it's hanging together, I'll give it a shot
7:20 okay, so now we're going to try this and we should get the exact same message right, there are errors, brand is required, price must be not negative.
7:27 Guess what— it's the same, so let's make price not negative, that's the first thing we've got to do, now it's nine thousand
7:35 okay brand is required field, like everything in our little dealership, it's Opel,
7:39 this should work, notice bad request, when I hit this it should say 201 created, oh but it did not, what did we do wrong, cannot parse for json,
7:46 that's right json is not like Python, it's not a fan of the single quotes, now 500 internal server error, non type has no object attribute id
7:58 what did we do wrong here, let's see, 500 server error, I am guessing— oh, I see, yeah, I see what we're doing wrong here
8:13 so we're getting the car back and car is actually what we did want to return right we want to return the one created by the repository
8:21 but the problem is in complete details, I'm not saying self.car, right there, and I will say car=self.car, okay, right this should work, right
8:33 pass validation, go— boom, car created and notice, Opeal concept 2020, lets grab the id, so we can go have a look, let's go over here and say
8:44 we want to duplicate that, and we'll go over here and get this yeah, it sure looks like we created it correctly
8:52 and maybe you noticed before these were strings, now they're not strings, we've made it a little bit nicer
8:58 because I had to add some work for the validation, it wouldn't compare a string number versus negative, it's less than zero.
9:05 All right, so this is looking like it's working pretty well, so let's just go back and look what we've got,
9:12 again, this view model thing is doing all of the work and it lets the validation become as complex
9:19 or when the creation and parsing become as complex as it needs to be without messing up our API,
9:26 and once you get used to working with these view models you know exactly what those three lines mean, okay.
9:32 So this is a really, really nice pattern, it is actually more useful when you're doing HTML stuff, because the templates usually require
9:39 many pieces of data right, templates want like maybe a car, a logged in user whether there's a sale price, like all sorts of stuff
9:46 and you can like use the view model to carry all that data over to the template but it is still very valuable even here in this API world as well.