Marshalling12:18 with Kenneth Love
The last step in creating a working API is controlling what data comes out of your API. You may need to take different data in than goes out or you might want to change how certain fields are created before they're sent to the client. Flask-RESTful's marshalling function and field definitions are the key here.
We have resources, we have routes and ACDP methods. 0:00 We have input validation. 0:03 Our basic API only needs one more piece added to it 0:04 before we can start experimenting with it. 0:08 We need to finish out the request response cycle and 0:10 create some useful responses from our methods. 0:13 We'll do this through marshalling, which is similar to serializing. 0:16 Basically we wanna take a Python object, like a string or integer, and 0:20 turn it into something that we can send safely over the Internet. 0:23 Most of the time this is really straightforward, 0:27 but you wouldn't need me if there weren't a few twists and turns. 0:29 Let's check it out. 0:32 So first of all, this video uses a new workspace because of the review arguments. 0:35 So be sure and relaunch it if you haven't done that already. 0:41 I wanna show these real quick. 0:44 So for ReviewList, we have an argument called course which 0:46 is of type inputs.positive cuz it has to be a positive number. 0:50 It's required and there's the help. 0:54 For rating I did an input of type int_range from 1 to 5. 0:57 It's also required so like that. 1:03 You can check out all of these in the docs. 1:05 I have put a link in the previous video to the inputs and 1:08 I'll put one in this one as well. 1:12 And then we also have comment which is not required is nullable and 1:13 has a default of an empty string, so cool. 1:18 The last thing the API needs for 1:22 right now at least, is a way to send back real actual data from the database. 1:24 And to create and update actual database records, and delete them of course. 1:29 So basically, well, everything. 1:34 But none of that is that difficult. 1:37 Before I can send back data I have to tell Flask RESTful 1:40 what the data should look like. 1:43 I have to specify how the input looks with reqparse and 1:45 how the output looks with, this is actually a little bit disappointing. 1:48 But just with a dictionary. 1:53 First though, I have to come up here and import this fields module. 1:55 And then I do a thing on here. 2:00 So this dictionary is going to describe all of the fields that I want to include 2:02 when my API gives a resource. 2:07 I'm doing this inside of courses because that's the one I like to start with. 2:09 So I'm gonna say the course_fields, these are all the fields that describe a course. 2:12 So we're gonna have the id and that's gonna be an integer. 2:19 We're gonna have the title which is a string, 2:23 we're gonna have the url which is a string. 2:27 And that one's a little surprising and I'll talk about that in just a second. 2:30 And we're gonna have reviews which is a list of strings. 2:33 Now you might be wondering about a couple of these, 2:39 why did I use fields.String for the url instead of fields.url? 2:42 Fields.url totally exists, you can look through the docs which again, I will link. 2:47 It's totally there, it exists. 2:51 But fields.url usually represents an internal URL in the API, so 2:54 it's a point to another record. 2:58 I want to just return whatever URL was saved for the course and 3:00 so that's why we're using string. 3:04 And this last one review is a list, yeah, it's a list of strings. 3:07 But why a list of strings when I just said that I can use the url field 3:14 to reference another endpoint. 3:18 Flask RESTfuls, lists and url fields don't seem to play nicely together. 3:20 I could not get them to do what I wanted, but this one worked and was fairly easy. 3:24 So this is the way that I'm recommending you do it. 3:29 All right, so now I'm gonna update this get method down here on course list. 3:33 I'm gonna have it get all the courses. 3:38 So courses = models.Course.select, so that'll pull out all the courses. 3:41 And I'm gonna return, 'courses' : courses. 3:47 Now this is a good start, but let's go see what happens when we get this. 3:55 Actually know what? 4:02 Let's use Postman, because that's what we're supposed to be using. 4:02 All right. 4:08 So let's get that. 4:10 And look we've got TypeError, it's a little hard to read here but 4:11 you can see that we have a TypeError. 4:14 See if I do preview, there we go. 4:17 So TypeError Course SELECT, blah, blah, blah, blah, blah is not JSON serializable. 4:18 So we can't turn this into JSON, 4:24 fair enough, so how do we handle that? 4:29 Well. 4:34 Flask RESTful has two methods, I'm gonna put 4:35 all these into, just getting awfully long. 4:40 All right, so we have two things here, we have marshal and we have marshal_with. 4:45 And we're gonna need both of those, so marshal and marshal_with is a decorator. 4:52 And it works just like marshal does, but 4:59 it allows you to not have to do a certain little bit of work. 5:02 So we had to bring in those two. 5:06 So now when you're using marshal, you provide it the record or 5:08 records and the fields that you defined for that resource. 5:12 So I have to do that for each of the records. 5:17 So what I'm gonna do here is let's make a new thing and 5:20 we can say courses = marshall course and 5:25 course fields for a course in that. 5:30 That line's way too long, so I'm gonna break it there. 5:35 All right, so marshal all of these things for every course that's inside of this. 5:40 And then we're gonna return that. 5:45 So now, let me test this again real quick. 5:47 And check it out, we got our information. 5:51 So cool, we got back pretty much everything. 5:54 The reviews field is null which is probably because there's 5:57 not actually a reviews attribute on the model. 6:01 And we don't have any reviews yet either. 6:03 There are couple of different ways that I could solve not having any reviews on 6:06 the model. 6:10 I could have property to the model, have a property defined named reviews. 6:12 And then that goes and fetches all the reviews but 6:16 that makes the model and the API tied together pretty tightly. 6:19 And I don't necessarily want them to be super tightly coupled. 6:24 I could also turn model instances into dictionaries and 6:27 then add a key which works. 6:31 Or I could do what I'm gonna do and I'm gonna add an attribute to each instance. 6:35 Now, why do it this way instead of the dictionary or model approach? 6:39 Like I said, 6:43 the model approach requires the model to know about the API as a limitation. 6:44 I don't like that, it's way too tightly coupled. 6:47 The dictionary one and 6:50 the attribute one that I'm gonna do, those are pretty much interchangeable. 6:51 So you could pick whichever one of those you like better. 6:54 I just happen to think the attribute is a little bit cleaner, so 6:57 that's the way I wanna do it. 7:01 So I'm gonna add a new method up here or functions, 7:03 sorry, function while not method. 7:07 That's gonna be called add_reviews and it's gonna take a course. 7:11 And then I'm gonna say course.reviews, so I'm gonna make a new attribute. 7:14 And then I'm gonna use the url_for method and 7:18 I'm gonna say resources.reviews.review. 7:21 So that's going to that end point. 7:25 And the id is gonna be equal to review.id for review in course.review_set. 7:27 And then I'll return the course. 7:35 And again this is long, so let's break it there. 7:36 That still feels kind of long, but it says I'm only at 72, so that's all right. 7:42 I need to import url_for and this actually comes from RESTful. 7:47 But I think you can use the one that Flask provides as well, 7:55 I don't believe it matters which one that you use. 7:57 And then I need to use this method, so I'm gonna marshal course but 8:00 first I'm gonna do add_reviews to course. 8:04 Okay, cool, so we're gonna pass the course in the add_reviews, 8:07 which adds the reviews and returns the course. 8:11 And then we're gonna pass the course into marshal which finds the reviews thing. 8:13 That makes sense? 8:18 It all just functions all the way down. 8:19 All right, so now let's try requesting this again and 8:22 reviews is now an empty list as it should be. 8:26 So awesome. 8:31 So that's the place where reviews are gonna go. 8:32 All right, so now I want to do, I'm gonna ignore post for now. 8:34 I'm gonna come down here and I'm gonna do the get for a single course. 8:38 So I'm gonna use marshal_with for this one, so 8:43 I can show you how the decorator works. 8:47 But before I do that, I need to have a way to get a course or 8:49 to throw a 404 if that course doesn't exist. 8:53 So let's come up here and add another method. 8:56 And we'll call this course_or_404 and course_id. 9:00 Sorry, I need to import more thing, I need to import abort. 9:06 Which abort just immediately ends a request. 9:10 All right, so we're gonna try course = models.Course.get, 9:14 models.Course.id is equal to course_id that was passed in. 9:19 Except models.Course.DoesNotExist. 9:26 So if the course doesn't exist, then I'm gonna run the abort function with 9:30 a status code of 404 and a message that is Course something does not exist. 9:36 And I'll format that with the course_id that they provided. 9:45 Otherwise, return the course, okay? 9:49 Really straightforward function there. 9:54 So down here, let's do return add_reviews, 9:58 course_or_404 and then the id that gets passed in. 10:03 And then we decorate this method with @marshal_with(course_fields). 10:10 So what this will do is this ends up returning a single record. 10:17 And so the marshal_with takes that record and marshals it using those fields. 10:22 So the exact same work we did before, 10:27 we could totally just do marshal and course_fields like that. 10:31 But doesn't really matter, [LAUGH] one way or the other. 10:37 They're equivalent, they're the exact same idea. 10:45 Marshal_with is really meant to be used when you're returning a single thing. 10:47 And marshal becomes more useful when you have to sort of 10:50 work on the model instance before it comes out. 10:55 Or you have to work on a lot of model instances all at once, 10:58 it seems like marshal's easier to use. 11:01 As you build these APIs, you'll start to be able to predict which one you should be 11:04 able to use whenever you're building a method or whatever. 11:08 So I added a course in the last video, which means I should be able 11:12 to go over here to /courses/1 and get that course back out. 11:17 And check it out, there's Python Collections and there's the URL, so 11:25 I got it. 11:29 Let me make sure that my 404 thing works, I'm gonna go to /2 which should not exist. 11:30 And yeah, it doesn't exist. 11:35 And it got an unexpected keyword message. 11:40 Does abort not expect message? 11:43 Well, you know what let's just take that off and we'll just say 404. 11:46 So send that and cool, we got a 404 and 11:51 the request url is not found on the server. 11:54 So great, that's what we want. 11:57 Now, I want you to see if you can do something similar 12:00 to what I've done here for courses to both ReviewList and review. 12:04 Just for the get and maybe a post method if you want. 12:08 In the next video, I'll show you my version of those and 12:12 we'll talk about put, delete and adding some headers. 12:14
You need to sign up for Treehouse in order to download course files.Sign up