RESTful and HTTP APIs in Pyramid Transcripts
Chapter: Customer Renderers
Lecture: Adapting non-standard types
0:01 So we have this csv renderer working super well right here.
0:04 However, one thing that is really not nice about it
0:08 is this bit about, if it's a car right,
0:11 we should not have to embed into the renderer
0:14 knowledge about these types, they should be separate they should be disjoint.
0:19 So, how do we do that? Well let me comment this out for a second
0:22 and just show that it's not going to be so much, okay, let's accept the csv type,
0:28 you can see that that is not great, right there, right no attribute keys
0:34 car has no attribute keys we saw this before;
0:36 so how do we address that problem?
0:38 Well,one would be you could just say look the csv only takes dictionaries
0:42 so in our api here, I could say all right, well instead of returning cars
0:47 what I'm going to return is we'll just do a little list comprehension here
0:51 and the list comprehension will convert a list of cars to a list of dictionaries,
0:54 so we'll say car for car in cars, what are we going to do the car
0:58 we're going to say to dict, right, that would actually work;
1:01 so if a rerun this, and re-request it, things should be back to good.
1:04 So that's one possibility, like you tell the renderer,
1:07 like the renderer just has these limitations
1:09 it only takes dictionaries and lists of dictionaries;
1:12 on the other hand this is also sort of polluting our api
1:15 how did we solve this with the json renderer— what we did was
1:18 we added an adapter, so adding an adapter that given a car it knows how to transform it.
1:24 Let's go and add that here, like this.
1:28 Let's tell it that it has an adapter and you know what,
1:30 it's probably going to use exactly the same type, okay,
1:34 so obviously this method doesn't exist, but PyCharm is happy to write it for us,
1:38 this will be type, let's just call it class like we're going to adapt from this
1:45 and this will be a method, okay,
1:48 so let's go and say this object is going to have initializer,
1:54 and in here it's going to have a dictionary of things that it can use called adapters.
2:00 And down here, we'll just say into this adapter
2:03 by the type of the object that we're going to pass in
2:07 so car or whatever, we want to use that as the hash and we got to put in the method.
2:12 So you give me a car here's the method to call and get the car back out,
2:15 you're going to give me some other type, you know, a customer or a user
2:20 here's the thing to call to convert it to something we can deal with
2:23 which is either straight up dictionaries or a list of dictionaries.
2:28 Now, the next thing we need to do is
2:30 somehow take this general stuff that we're doing or this specific stuff
2:34 and make this more general, let's select all of this and make this a method
2:37 so we'll call this adapt type, it's going to take the value
2:43 and it's going to return back something
2:45 and maybe this looks like that's kind of part of this adaptation stuff here
2:50 ok so we want to come in and we want to say well if it's one of these things
2:53 now how do we generalize this, right, we have this adapter dictionary
2:57 and somehow we need to convert it
2:59 so here this question basically says is that a type that you know how to adapt
3:03 and then this will say okay we'll convert it to a list of that thing adapted
3:06 all right let's try to create a generalized equivalent,
3:11 so we'll say if type of value is in our adapters,
3:15 that means there is a function that we know how to change this
3:19 right, so than we'll say value equals now we just need to adapt the value
3:22 so we'll say self.adapters, make this little simpler up here,
3:27 like we don't want to keep typing that and keep computing that,
3:30 so we'll give that, and the adapter takes—
3:34 actually I think that adapter takes two values and it returns back the adapted value.
3:40 Okay, so before we had it hard coded to just the car,
3:44 and now we have this dictionary of adapters
3:48 which have a bunch of types, not just car potentially
3:51 and they have functions which we can call
3:53 their value is a function which we can call that returns the adapted thing,
3:57 this is because we want to get everything into a list of stuff.
4:03 And then, we are going to do this and now we're going to come here and say
4:07 okay, same thing, but really this value is basically the same
4:10 so we want to create the type of the first item and we could even maybe say
4:14 alright, we're just going to assume the type is the first one and not recompute this
4:17 but we're not worried about performance, yet, let's just get this working.
4:21 So then we're going to ask is the type in there
4:23 and then how did we have this before, we had a value of index
4:26 and then we ran the adapter function on item, like so.
4:31 All right, so if we run this now and we undo this, just return cars again,
4:39 what are we going to get, not something amazing, car has no item keys;
4:45 why did this happen— well just maybe you know in our minds
4:48 we're like oh we could have an adapter for cars right
4:51 did we add it, oh it looks like I made a super small mistake here
4:57 look on line 50, what do you think that comes back as—
5:00 well on line 48 we know it's a list, and a list is not what we're looking for,
5:03 we just really wanted to have the first value like what does the list contain
5:07 so let's make this and call this first, just so it's really clear
5:11 and let's actually move that outside of the list here
5:14 that will also be better for performance
5:17 and we could also say adapter equals, put this up here
5:24 we'll stay if not, I'll say if adapter, I think that's a little nicer.
5:28 No reason to compute that over and over and over again, right,
5:31 just once and then call it on every item in the list; okay, let's try this again.
5:38 Send, key error list, where are we getting this list error?
5:46 Oh I just copied this, this should be type of first
5:50 and we don't need that again, like this, ok try one more time,
6:02 yes, okay they do want to value there but obviously this is not none,
6:09 alright, look at that how cool, sorry about the little glitch there
6:13 but this is working beautifully, if we go over here
6:18 you can see down here we've got our csv coming through
6:21 and in the api side, remember what we did was
6:26 we are just now returning a list of cars
6:29 we kind of fixed it by letting the renderer be dumber at the beginning
6:34 but then we said no no let's actually let it return arbitrary objects
6:37 and long as somewhere in the system we tell it like hey if you have a car
6:41 here's how you get to a dictionary and once you have dictionaries or lists of dictionaries
6:45 you can totally deal with that, and the way we taught it was
6:48 we added this adapter, just like we did for json,
6:51 in fact, it's highly inspired by the way the built in one works
6:56 that you can add this adapter it looks it up by type and so on.
6:59 We'll see that this is really nice because while you might look at this and say
7:04 you know this is no big deal, like we can just do that, right,
7:06 I don't need all this complexity of adapting types and whatnot;
7:10 it turns out that sometimes you really do need it,
7:13 it's great to just do it on line 15
7:15 but any time you return to car down here, down here,
7:19 all of these will have to do like this translation
7:22 and then what if you want to change how that works or something,
7:25 it becomes you know, sort of something
7:27 that works its way through all the parts of your api
7:29 and it's better to just have this one place,
7:31 right here we say if you need to take a car to a dictionary
7:35 teach a renderer to do it, it goes there,
7:38 just like it was really nice to do this here,
7:41 and then we could return arbitrary objects involving cars or lists of cars and so on
7:44 and the json renderer would deal with it, same thing here.
7:48 So let's just review our adapt type function here.
7:51 We're given some type and we may need to change it into another type right,
7:56 we saw we're given an individual car we need to convert that into a list of cars;
8:01 if we're giving a list and it's not a list of dictionaries
8:05 we need to convert it to a list of dictionaries.
8:07 So we set up this adapter concept, we can register many things
8:10 that can be converted to dictionaries, and then we just go through and say
8:13 look if the type of this thing that we're working with,
8:17 if it's an individual instance not a list and we could convert it,
8:21 then we're going to convert it to a list of one item by adapting that value.
8:25 But if it's a list, let's go through every item in the list
8:27 and make sure that it is something we can work with,
8:30 so here we get the first one and we say do we have an adapter
8:34 in this case it's a list of cars, and it says yep I know how to convert cars
8:39 so it just goes through the list assuming the list is homogenous,
8:43 one thing I suppose would be fun to test is if we go up here to the top again
8:47 and just show that we can still return other types
8:50 still show that we can return this list of dictionaries
8:53 and that somehow won't break the system, I don't think it will, let's find out.
8:56 Yes, yes it did break it; where did it break— all right,
9:00 yes, this seems to be a get, so what do we do wrong here—
9:06 on this one, we are basically assuming it is in here
9:10 and of course, why would we test for it, if we were doing that
9:12 so let's do the more gentle friendly get me this type
9:16 and then we're already testing for it.
9:19 So one more time, boom, we're returning dictionaries
9:22 and so it's you know, I don't need to adapt dictionaries,
9:24 you already can work with those.
9:26 If we change this to say no, no, return cars, we go return cars, okay.
9:31 So now, we have or csv entirely built in a general way right,
9:37 the csv as far as its work goes, all it really cares or knows about are dictionaries
9:43 and maybe we want to put some kind of test in here
9:45 and say you know, I was given some type, I don't know about it
9:49 and it should raise some type of exception, right,
9:52 so we have this, maybe let's go ahead and say first equals value of zero
9:57 say if not is instance first dict,
10:02 well then we have a problem, it could not convert type or something like that
10:08 and maybe we'll just say what the type was here
10:11 all right, a little better error message,
10:14 we'll do a type of first, so it should still work
10:20 and then maybe we can send it something that it doesn't expect
10:24 just to show you know, it's going to be able to detect this
10:28 so let's return a list of tuples let's say,
10:31 it shouldn't know what to do with that, let's try this—
10:34 built in error could not convert type class tuple.
10:38 Perfect, so now I think we've got a pretty good system here
10:42 make sure l put it back for you in a way that works, excellent.
10:45 Okay, so we've built this custom csv renderer,
10:50 we've used this adapter concept to give it lots of flexibility
10:53 to take many, many different types and not force all the ways
10:56 in which we use it through our application
10:59 to know that that transformation is required
11:01 we just add the adapter in the site setup and we're golden.
11:04 So while this is fun, let's actually add one more type, let's work with images as well,
11:09 so we'll be able to go to the car and say hey car I would like your image.