1 00:00:00,640 --> 00:00:03,890 We have resources, we have routes and ACDP methods. 2 00:00:03,890 --> 00:00:04,928 We have input validation. 3 00:00:04,928 --> 00:00:08,480 Our basic API only needs one more piece added to it 4 00:00:08,480 --> 00:00:10,730 before we can start experimenting with it. 5 00:00:10,730 --> 00:00:13,570 We need to finish out the request response cycle and 6 00:00:13,570 --> 00:00:16,440 create some useful responses from our methods. 7 00:00:16,440 --> 00:00:20,328 We'll do this through marshalling, which is similar to serializing. 8 00:00:20,328 --> 00:00:23,660 Basically we wanna take a Python object, like a string or integer, and 9 00:00:23,660 --> 00:00:27,560 turn it into something that we can send safely over the Internet. 10 00:00:27,560 --> 00:00:29,550 Most of the time this is really straightforward, 11 00:00:29,550 --> 00:00:32,410 but you wouldn't need me if there weren't a few twists and turns. 12 00:00:32,410 --> 00:00:32,910 Let's check it out. 13 00:00:35,040 --> 00:00:41,380 So first of all, this video uses a new workspace because of the review arguments. 14 00:00:41,380 --> 00:00:44,091 So be sure and relaunch it if you haven't done that already. 15 00:00:44,091 --> 00:00:46,186 I wanna show these real quick. 16 00:00:46,186 --> 00:00:50,280 So for ReviewList, we have an argument called course which 17 00:00:50,280 --> 00:00:54,080 is of type inputs.positive cuz it has to be a positive number. 18 00:00:54,080 --> 00:00:56,760 It's required and there's the help. 19 00:00:57,810 --> 00:01:03,260 For rating I did an input of type int_range from 1 to 5. 20 00:01:03,260 --> 00:01:05,730 It's also required so like that. 21 00:01:05,730 --> 00:01:08,190 You can check out all of these in the docs. 22 00:01:08,190 --> 00:01:12,240 I have put a link in the previous video to the inputs and 23 00:01:12,240 --> 00:01:13,990 I'll put one in this one as well. 24 00:01:13,990 --> 00:01:18,350 And then we also have comment which is not required is nullable and 25 00:01:18,350 --> 00:01:21,120 has a default of an empty string, so cool. 26 00:01:22,400 --> 00:01:24,670 The last thing the API needs for 27 00:01:24,670 --> 00:01:29,110 right now at least, is a way to send back real actual data from the database. 28 00:01:29,110 --> 00:01:34,700 And to create and update actual database records, and delete them of course. 29 00:01:34,700 --> 00:01:37,550 So basically, well, everything. 30 00:01:37,550 --> 00:01:40,330 But none of that is that difficult. 31 00:01:40,330 --> 00:01:43,280 Before I can send back data I have to tell Flask RESTful 32 00:01:43,280 --> 00:01:45,150 what the data should look like. 33 00:01:45,150 --> 00:01:48,710 I have to specify how the input looks with reqparse and 34 00:01:48,710 --> 00:01:53,410 how the output looks with, this is actually a little bit disappointing. 35 00:01:53,410 --> 00:01:55,190 But just with a dictionary. 36 00:01:55,190 --> 00:01:58,840 First though, I have to come up here and import this fields module. 37 00:02:00,200 --> 00:02:02,780 And then I do a thing on here. 38 00:02:02,780 --> 00:02:07,980 So this dictionary is going to describe all of the fields that I want to include 39 00:02:07,980 --> 00:02:09,360 when my API gives a resource. 40 00:02:09,360 --> 00:02:12,408 I'm doing this inside of courses because that's the one I like to start with. 41 00:02:12,408 --> 00:02:19,461 So I'm gonna say the course_fields, these are all the fields that describe a course. 42 00:02:19,461 --> 00:02:23,736 So we're gonna have the id and that's gonna be an integer. 43 00:02:23,736 --> 00:02:27,041 We're gonna have the title which is a string, 44 00:02:27,041 --> 00:02:30,195 we're gonna have the url which is a string. 45 00:02:30,195 --> 00:02:33,331 And that one's a little surprising and I'll talk about that in just a second. 46 00:02:33,331 --> 00:02:38,350 And we're gonna have reviews which is a list of strings. 47 00:02:39,630 --> 00:02:42,955 Now you might be wondering about a couple of these, 48 00:02:42,955 --> 00:02:47,222 why did I use fields.String for the url instead of fields.url? 49 00:02:47,222 --> 00:02:51,760 Fields.url totally exists, you can look through the docs which again, I will link. 50 00:02:51,760 --> 00:02:54,420 It's totally there, it exists. 51 00:02:54,420 --> 00:02:58,770 But fields.url usually represents an internal URL in the API, so 52 00:02:58,770 --> 00:03:00,162 it's a point to another record. 53 00:03:00,162 --> 00:03:04,870 I want to just return whatever URL was saved for the course and 54 00:03:04,870 --> 00:03:07,370 so that's why we're using string. 55 00:03:07,370 --> 00:03:14,110 And this last one review is a list, yeah, it's a list of strings. 56 00:03:14,110 --> 00:03:18,410 But why a list of strings when I just said that I can use the url field 57 00:03:18,410 --> 00:03:20,280 to reference another endpoint. 58 00:03:20,280 --> 00:03:24,040 Flask RESTfuls, lists and url fields don't seem to play nicely together. 59 00:03:24,040 --> 00:03:29,790 I could not get them to do what I wanted, but this one worked and was fairly easy. 60 00:03:29,790 --> 00:03:32,320 So this is the way that I'm recommending you do it. 61 00:03:33,430 --> 00:03:38,730 All right, so now I'm gonna update this get method down here on course list. 62 00:03:38,730 --> 00:03:41,320 I'm gonna have it get all the courses. 63 00:03:41,320 --> 00:03:46,550 So courses = models.Course.select, so that'll pull out all the courses. 64 00:03:47,600 --> 00:03:54,179 And I'm gonna return, 'courses' : courses. 65 00:03:55,340 --> 00:04:02,370 Now this is a good start, but let's go see what happens when we get this. 66 00:04:02,370 --> 00:04:02,951 Actually know what? 67 00:04:02,951 --> 00:04:08,900 Let's use Postman, because that's what we're supposed to be using. 68 00:04:08,900 --> 00:04:09,400 All right. 69 00:04:10,600 --> 00:04:11,870 So let's get that. 70 00:04:11,870 --> 00:04:14,870 And look we've got TypeError, it's a little hard to read here but 71 00:04:14,870 --> 00:04:16,085 you can see that we have a TypeError. 72 00:04:17,170 --> 00:04:18,980 See if I do preview, there we go. 73 00:04:18,980 --> 00:04:24,880 So TypeError Course SELECT, blah, blah, blah, blah, blah is not JSON serializable. 74 00:04:24,880 --> 00:04:29,221 So we can't turn this into JSON, 75 00:04:29,221 --> 00:04:34,680 fair enough, so how do we handle that? 76 00:04:34,680 --> 00:04:35,296 Well. 77 00:04:35,296 --> 00:04:40,664 Flask RESTful has two methods, I'm gonna put 78 00:04:40,664 --> 00:04:45,901 all these into, just getting awfully long. 79 00:04:45,901 --> 00:04:52,729 All right, so we have two things here, we have marshal and we have marshal_with. 80 00:04:52,729 --> 00:04:59,980 And we're gonna need both of those, so marshal and marshal_with is a decorator. 81 00:04:59,980 --> 00:05:02,110 And it works just like marshal does, but 82 00:05:02,110 --> 00:05:06,300 it allows you to not have to do a certain little bit of work. 83 00:05:06,300 --> 00:05:08,770 So we had to bring in those two. 84 00:05:08,770 --> 00:05:12,610 So now when you're using marshal, you provide it the record or 85 00:05:12,610 --> 00:05:17,920 records and the fields that you defined for that resource. 86 00:05:17,920 --> 00:05:20,530 So I have to do that for each of the records. 87 00:05:20,530 --> 00:05:25,320 So what I'm gonna do here is let's make a new thing and 88 00:05:25,320 --> 00:05:30,670 we can say courses = marshall course and 89 00:05:30,670 --> 00:05:35,170 course fields for a course in that. 90 00:05:35,170 --> 00:05:40,808 That line's way too long, so I'm gonna break it there. 91 00:05:40,808 --> 00:05:45,451 All right, so marshal all of these things for every course that's inside of this. 92 00:05:45,451 --> 00:05:47,870 And then we're gonna return that. 93 00:05:47,870 --> 00:05:50,240 So now, let me test this again real quick. 94 00:05:51,700 --> 00:05:54,570 And check it out, we got our information. 95 00:05:54,570 --> 00:05:57,050 So cool, we got back pretty much everything. 96 00:05:57,050 --> 00:06:01,100 The reviews field is null which is probably because there's 97 00:06:01,100 --> 00:06:03,780 not actually a reviews attribute on the model. 98 00:06:03,780 --> 00:06:06,511 And we don't have any reviews yet either. 99 00:06:06,511 --> 00:06:10,916 There are couple of different ways that I could solve not having any reviews on 100 00:06:10,916 --> 00:06:12,120 the model. 101 00:06:12,120 --> 00:06:16,490 I could have property to the model, have a property defined named reviews. 102 00:06:16,490 --> 00:06:19,980 And then that goes and fetches all the reviews but 103 00:06:19,980 --> 00:06:24,030 that makes the model and the API tied together pretty tightly. 104 00:06:24,030 --> 00:06:27,710 And I don't necessarily want them to be super tightly coupled. 105 00:06:27,710 --> 00:06:31,860 I could also turn model instances into dictionaries and 106 00:06:31,860 --> 00:06:35,080 then add a key which works. 107 00:06:35,080 --> 00:06:39,910 Or I could do what I'm gonna do and I'm gonna add an attribute to each instance. 108 00:06:39,910 --> 00:06:43,333 Now, why do it this way instead of the dictionary or model approach? 109 00:06:43,333 --> 00:06:44,030 Like I said, 110 00:06:44,030 --> 00:06:47,310 the model approach requires the model to know about the API as a limitation. 111 00:06:47,310 --> 00:06:50,470 I don't like that, it's way too tightly coupled. 112 00:06:50,470 --> 00:06:51,820 The dictionary one and 113 00:06:51,820 --> 00:06:54,710 the attribute one that I'm gonna do, those are pretty much interchangeable. 114 00:06:54,710 --> 00:06:57,660 So you could pick whichever one of those you like better. 115 00:06:57,660 --> 00:07:01,630 I just happen to think the attribute is a little bit cleaner, so 116 00:07:01,630 --> 00:07:02,400 that's the way I wanna do it. 117 00:07:03,530 --> 00:07:07,870 So I'm gonna add a new method up here or functions, 118 00:07:07,870 --> 00:07:11,080 sorry, function while not method. 119 00:07:11,080 --> 00:07:13,439 That's gonna be called add_reviews and it's gonna take a course. 120 00:07:14,460 --> 00:07:17,410 And then I'm gonna say course.reviews, so I'm gonna make a new attribute. 121 00:07:18,840 --> 00:07:21,870 And then I'm gonna use the url_for method and 122 00:07:21,870 --> 00:07:25,010 I'm gonna say resources.reviews.review. 123 00:07:25,010 --> 00:07:27,450 So that's going to that end point. 124 00:07:27,450 --> 00:07:33,909 And the id is gonna be equal to review.id for review in course.review_set. 125 00:07:35,200 --> 00:07:36,660 And then I'll return the course. 126 00:07:36,660 --> 00:07:42,011 And again this is long, so let's break it there. 127 00:07:42,011 --> 00:07:45,750 That still feels kind of long, but it says I'm only at 72, so that's all right. 128 00:07:47,310 --> 00:07:53,190 I need to import url_for and this actually comes from RESTful. 129 00:07:55,030 --> 00:07:57,760 But I think you can use the one that Flask provides as well, 130 00:07:57,760 --> 00:08:00,290 I don't believe it matters which one that you use. 131 00:08:00,290 --> 00:08:04,728 And then I need to use this method, so I'm gonna marshal course but 132 00:08:04,728 --> 00:08:07,775 first I'm gonna do add_reviews to course. 133 00:08:07,775 --> 00:08:11,210 Okay, cool, so we're gonna pass the course in the add_reviews, 134 00:08:11,210 --> 00:08:13,910 which adds the reviews and returns the course. 135 00:08:13,910 --> 00:08:18,410 And then we're gonna pass the course into marshal which finds the reviews thing. 136 00:08:18,410 --> 00:08:19,270 That makes sense? 137 00:08:19,270 --> 00:08:22,041 It all just functions all the way down. 138 00:08:22,041 --> 00:08:26,814 All right, so now let's try requesting this again and 139 00:08:26,814 --> 00:08:31,280 reviews is now an empty list as it should be. 140 00:08:31,280 --> 00:08:32,701 So awesome. 141 00:08:32,701 --> 00:08:34,881 So that's the place where reviews are gonna go. 142 00:08:34,881 --> 00:08:38,620 All right, so now I want to do, I'm gonna ignore post for now. 143 00:08:38,620 --> 00:08:42,620 I'm gonna come down here and I'm gonna do the get for a single course. 144 00:08:43,790 --> 00:08:47,530 So I'm gonna use marshal_with for this one, so 145 00:08:47,530 --> 00:08:49,280 I can show you how the decorator works. 146 00:08:49,280 --> 00:08:53,500 But before I do that, I need to have a way to get a course or 147 00:08:53,500 --> 00:08:56,340 to throw a 404 if that course doesn't exist. 148 00:08:56,340 --> 00:08:58,400 So let's come up here and add another method. 149 00:09:00,100 --> 00:09:06,200 And we'll call this course_or_404 and course_id. 150 00:09:06,200 --> 00:09:09,040 Sorry, I need to import more thing, I need to import abort. 151 00:09:10,900 --> 00:09:14,250 Which abort just immediately ends a request. 152 00:09:14,250 --> 00:09:19,709 All right, so we're gonna try course = models.Course.get, 153 00:09:19,709 --> 00:09:25,080 models.Course.id is equal to course_id that was passed in. 154 00:09:26,590 --> 00:09:30,940 Except models.Course.DoesNotExist. 155 00:09:30,940 --> 00:09:36,400 So if the course doesn't exist, then I'm gonna run the abort function with 156 00:09:36,400 --> 00:09:44,170 a status code of 404 and a message that is Course something does not exist. 157 00:09:45,660 --> 00:09:47,970 And I'll format that with the course_id that they provided. 158 00:09:49,910 --> 00:09:54,820 Otherwise, return the course, okay? 159 00:09:54,820 --> 00:09:56,720 Really straightforward function there. 160 00:09:58,690 --> 00:10:03,820 So down here, let's do return add_reviews, 161 00:10:03,820 --> 00:10:10,750 course_or_404 and then the id that gets passed in. 162 00:10:10,750 --> 00:10:17,430 And then we decorate this method with @marshal_with(course_fields). 163 00:10:17,430 --> 00:10:21,070 So what this will do is this ends up returning a single record. 164 00:10:22,070 --> 00:10:27,510 And so the marshal_with takes that record and marshals it using those fields. 165 00:10:27,510 --> 00:10:31,275 So the exact same work we did before, 166 00:10:31,275 --> 00:10:37,838 we could totally just do marshal and course_fields like that. 167 00:10:37,838 --> 00:10:45,340 But doesn't really matter, [LAUGH] one way or the other. 168 00:10:45,340 --> 00:10:47,521 They're equivalent, they're the exact same idea. 169 00:10:47,521 --> 00:10:50,921 Marshal_with is really meant to be used when you're returning a single thing. 170 00:10:50,921 --> 00:10:55,114 And marshal becomes more useful when you have to sort of 171 00:10:55,114 --> 00:10:58,910 work on the model instance before it comes out. 172 00:10:58,910 --> 00:11:01,810 Or you have to work on a lot of model instances all at once, 173 00:11:01,810 --> 00:11:04,410 it seems like marshal's easier to use. 174 00:11:04,410 --> 00:11:08,330 As you build these APIs, you'll start to be able to predict which one you should be 175 00:11:08,330 --> 00:11:12,800 able to use whenever you're building a method or whatever. 176 00:11:12,800 --> 00:11:17,910 So I added a course in the last video, which means I should be able 177 00:11:17,910 --> 00:11:23,890 to go over here to /courses/1 and get that course back out. 178 00:11:25,600 --> 00:11:29,879 And check it out, there's Python Collections and there's the URL, so 179 00:11:29,879 --> 00:11:30,451 I got it. 180 00:11:30,451 --> 00:11:35,971 Let me make sure that my 404 thing works, I'm gonna go to /2 which should not exist. 181 00:11:35,971 --> 00:11:40,940 And yeah, it doesn't exist. 182 00:11:40,940 --> 00:11:43,420 And it got an unexpected keyword message. 183 00:11:43,420 --> 00:11:45,110 Does abort not expect message? 184 00:11:46,820 --> 00:11:51,650 Well, you know what let's just take that off and we'll just say 404. 185 00:11:51,650 --> 00:11:54,438 So send that and cool, we got a 404 and 186 00:11:54,438 --> 00:11:57,650 the request url is not found on the server. 187 00:11:57,650 --> 00:12:00,690 So great, that's what we want. 188 00:12:00,690 --> 00:12:04,520 Now, I want you to see if you can do something similar 189 00:12:04,520 --> 00:12:08,850 to what I've done here for courses to both ReviewList and review. 190 00:12:08,850 --> 00:12:12,050 Just for the get and maybe a post method if you want. 191 00:12:12,050 --> 00:12:14,800 In the next video, I'll show you my version of those and 192 00:12:14,800 --> 00:12:17,690 we'll talk about put, delete and adding some headers.