Write an API for Almost Anything... by Charlotte Mays

This video features Charlotte Mays at DjangoCon US 2017 in Spokane, Washington, USA.

Write an API for Almost Anything... by Charlotte Mays
0:24:30
Published September 6, 2017
16,185 views
385 likes

DjangoCon US 2017 - Write an API for Almost Anything (or The Amazing Power and Flexibility of Django Rest Framework) by Charlotte Mays

This talk will feature a few off-the-beaten-path applications of APIs. Since the combination of Django and DRF makes it so easy to get a simple API running, it becomes a very powerful, flexible, and expandable tool for a variety of uses. The only thing these applications may have in common is their need to share data across the web. Whether you have not yet tested the waters of Django Rest Framework or you are a DRF veteran, this talk will inspire you to think both big and small when considering its potential uses.

This talk was presented at: https://2017.djangocon.us/talks/write-an-api-for-almost-anything-or-the-amazing-power-and-flexibility-of-django-rest-framework/

LINKS:
Follow Charlotte Mays 👇
On Twitter: https://twitter.com/charlottecodes

Follow DjangCon US 👇
https://twitter.com/djangocon

Follow DEFNA 👇
https://twitter.com/defnado
https://www.defna.org/

Summary

Charlotte Mays explains that an API is code that lets software programs communicate, giving applications flexibility to expose data and operations to users, internal services, JavaScript front ends, or non-web clients. Using Django REST Framework, she shows how models, serializers, viewsets, and routers combine to provide CRUD endpoints, then covers HTTP methods, authentication and permissions, documentation, and automated tests. She argues that even small, unexpected user needs can justify an API, because an API lets others build features the original application did not anticipate.

Key takeaways

  • Django REST Framework can add an API layer to an existing Django application without replacing its models or database code.
  • Serializers define the fields and data representation, viewsets handle operations, and routers connect those operations to URLs.
  • CRUD endpoints conventionally use POST, GET, PUT or PATCH, and DELETE, with routers generating the corresponding URL and method handling.
  • Authentication, read-only viewsets, and per-action permissions can limit what API users are allowed to do.
  • API documentation should specify each endpoint’s URL, HTTP method, parameters, operation, and response format.
  • Django REST Framework’s test tools make it straightforward to test list, detail, and creation endpoints as part of the normal test suite.

Summarised automatically from the transcript.

Transcript

4,176 words · auto-generated Show

Automatically transcribed, so expect mistakes in names and technical terms.

0:14

Speaker 1: All right, can everybody hear me okay? Great. Okay, so I'm going to talk about writing APIs for almost anything. I am, as was just said, a web developer at Cactus Group. I am one of the organizers of the PyLadies group in our area, and most importantly for this talk, I am a builder and user of API. If you'd like to follow along with my slides, the link is here and I will also have that link again at the end of the presentation. So if you just want it for reference later, don't worry, you can get it at the end. So we should probably start by defining our terms. What exactly is an API? Well it stands for Application Programming Interface, but to be perfectly honest, that probably doesn't give you any more information than you had before. More importantly, it is code that lets two software programs communicate with each other.

1:03

Speaker 1: That's the really key piece about what an API is. And that's what makes it both powerful and useful. So we like APIs because they give us flexibility. Once you've got an API in place, you've got access to all those basic functions. reading your data, updating your data, et cetera, without the entire structure of what you expect the workflow to be. And so that gives you flexibility to be able to do different different kinds of things. It also gives you more access. Obviously we think of APIs largely in terms of users being able to access directly But you also can use an API internally and make it not even expose it to the outside world, but use it with two different pieces of code on one server or on two different servers that just talk to each other

1:51

Speaker 1: so that you have better access. To your own systems. And you can also use this for future proofing. Once you've got an API in place, that gives you the ability when suddenly a customer has a need right now that we've got to do something a little different. You've got an API in place that gives you that flexibility again, makes it easier to implement new things without as much complexity. So to give you an example of when an API might come in handy, I have a friend, this is a real story, massage therapist who needed his schedule information to be shareable without the client information attached. So basically what times he had appointments without the client. Client names. His scheduling software, while it did a lot of things, it didn't do this, but it did have an API. So we were able to put together a small script that can pull that schedule in front of

2:39

Speaker 1: information via that API, strip out the client information, and then post that appointment information up to a shareable calendar. The API, in other words, made it possible for him to create an otherwise non-existent feature from the perspective of a user, not a developer. You also can use APIs for non-web applications. I'll just mention this briefly. We often think of Django as being a web framework, and that is its primary purpose, but that doesn't mean that its power and flexibility is limited to the web. As an example, I gave a talk at a game conference about how this could be used create a game back end so that the front end, the user experience, the gameplay could all be done without having to worry about the shared state of the game for multiplayer.

3:30

Speaker 1: uh types of applications. And you can see that code up on my GitHub here. You also can use APIs for internal separation. of code within your own applications. Modern applications are heavily reliant on JavaScript to be highly interactive and responsive. And so that requires JavaScript. You can use an API to separate your Django code from your JavaScript code, which results in having cleaner code because you don't have that all tangled together in spaghetti code. And it also makes it easier to use a lot of JavaScript frameworks. That are going to often be built around building a single-page app, which isn't quite the way Django templates work. So if you have your code built with an API, then you can have that JavaScript framework built as a single page app, and it can retrieve the data and context that it needs with simple API requests.

4:22

Speaker 1: And again, this can be on the same server, so you're not really the Dealing with any sort of latency here. All right, so how do we do this? There are a lot of ways to build an API, there are a lot of packages available to do it. This talk is gonna focus on J Django Rest framework. Django Rest Framework sits nicely on top of existing Django code and has a very thorough feature set. Cactus Group uses Django Rest Framework. All the time. We like it enough we actually even sponsor it. Let me show you why. So here's the anatomy of a Django Rest framework API. At the bottom here, you've got your existing Django models, which you know Django takes care of your database, all that sort of thing. On top of that, the next layer you've got is your serializer. This is a piece of Django

5:08

Speaker 1: Rest framework that is just going to to take your model information and parse it into a format in going in both directions that the view set can work with. View set is the next layer here, and that is what handles, okay, am I creating a new instance? Am I updating an instance? Do you just want a list of the instance? The view set handles figuring out what information needs to either come out of the database or go into the database. And control that. And then the last thing on top of that is the router, and that's what handles the actual access via the URLs. So where do we put these things? This is my convention. I like to have a file called this is all within my application

5:54

Speaker 1: within my app, within my Django app. I like to have a file called serializers. py that has my serial I like to put my view sets directly into views. py. If you want separation, if you're still using Django templates and you want separation. You could also put this into a file called api. py. It would work just fine that way too. And then the router is going to go right into your URLs. py. This can be inside the app or it can be your global URLs. py, either way. Alright, so let's start at the bottom and talk about the serializer. So this is the serializer. This is it. So we're gonna import serializers from REST framework. We're gonna import our model from our models file. We're gonna create a serializer that's just subclassing the Jenga

6:42

Speaker 1: Rest framework. Framework model serializer, give it a meta class, tell it what the model is, and tell it what fields we want included. Any field that's not listed here The API is just going to ignore. So if you have private internal fields, you can leave them out of the serializer and they won't be exposed. Now let's move up a step in our ladder to the view set. So we're going to import that view set, we're going to import our model again, and we're going to import that serializer that we just created. Then we're gonna again subclass the model view set from Jenger S framework, tell it what our query set is. In this case, I'm just using all of the objects. that I have for my model. You could do different view sets

7:28

Speaker 1: for different types of, you know, different subsets of your data if you wanted to have different functionalities. But in this example, we're just going to use all of it. And then you tell it what serializer class. That's just going to be your serializer that you created. And finally, let's put the router on top. And this is going to nest just right within your existing Django URLs code. I have a convention that I like to import the views As a namespace things, because if you start putting all this, especially if you put this in a global URLs file, but either way, it makes it much easier to read When you look down here, we're going to define our router. Router equals routers. default router. That's just going to initialize it using

8:13

Speaker 1: The Jenga Rest framework router structure. And then we're going to do we're going to register our model with this. So routers. router. register. We're going to give it a namespace. I'm just giving it the name of the model in this case, but it can be any anything, and then you're going to tell it where the viewset is. Django Rest framework will take care of parsing the different URLs and the different methods that need to happen here. And then we just have to include our router. URLs. If you had other models, other view sets that you were importing, you would just need an extra register. line for each of those. The rest of this would stay the same way. And at the very bottom here I've got one other thing that's handy to have on there is the API auth which gives you access to

8:59

Speaker 1: the Jenga Rest framework built-in browsable APIs, you can click around and see what the functionality of your API is for learning the structure. All right, so when we're accessing our API, uh API structure is typically done using the CRUD acronym, CREATE, READ, UPDE, and DELETE. And we're going to use specific HTTP methods so that Django Rest framework knows what we're trying to accomplish. So when we're trying to create an instance, we're going to do a post. When we're trying to read, either get a list or get a detail view, we're going to use a git. For update, we're going to use either a put or a patch. Difference between these is put is going to expect all of the fields, just as if you were doing a post.

9:44

Speaker 1: Whereas patch will just take whatever fields you gave me. I'm going to assume those are the ones that change. And everything else, I'm just gonna leave the way it was. And then finally, delete uses the HTTP delete method. There are more HTTP methods than this, but these are all you need to know about for this particular Functionality. So just to give you a few examples, I'm just reprinting our register line from the URLs. py here so you can reference it. The only thing that we really need to reference here is the my model. namespace that we gave it. So this is going to translate into if I do a get HTTP request to myapp. com slash my model, it's going to give me a list of the instances. That's going to be based on that serializer.

10:31

Speaker 1: If I do a post to that same URL, just app. com slash my model, then it's going to create a new instance. It's going to expect me to be passing the data to create a new instance. If I do a get to my model slash an ID number, then it's going to get me the details for the instance that has that ID. If I do a delete to that same detail URL, then it's going to delete that instance. These are not a this is not an exhaustive list of the ways you can do it. It's just a sample to give you an idea of how this works. More detail can be found at Django Rest Frameworks documentation, which I put a link to here. So what if you don't want your users doing all this? Maybe you don't want your users directly having access to delete instances.

11:17

Speaker 1: Well there's different options. You can either put a layer of authentication on top. That layer of authentication will use the same authentication. as your Django user model. So if you want to restrict access, you can restrict access in exactly the same way you've already got it restricted on your templates. and so on. So whatever a user doesn't have access to normally they won't have access to through Django Rest framework. You also can do just read-only view sets. Django has built-in read only views Only view sets. So if you want to let people access information but not update anything or delete anything, that's a one-line change from what I've just shown. you. And you can also restrict specific actions. So maybe the only thing you don't want users doing is deleting.

12:03

Speaker 1: You can just restrict that specific action at the view set level. And all of this information again Again, Django Rest framework has fantastic documentation about how to customize all these different things. But I'm just trying to give you an idea of what the power of this is Speaking of documentation, your API will need documentation because nobody's going to be able to make use of it if they don't have any documentation of it. This is key to usability. Even if you're only using it internally, your developers will thank you if you have documentation. But there's a very specific structure. that we need here. This isn't going to be highly variable because this API is going to be constructed the same way over and over again. You'll need to give the URL and HTTP method what operation is performing.

12:49

Speaker 1: When you hit that URL with that method, what parameters it expects to receive, and what data format will be returned. So you can just create a docs URL in your URLs. py and do include docs URLs imported straight from Jenkins. And it will create your API documentation at that URL, and you are off and running. I hope that you also are sitting here thinking, but how will I test this? Because we all should be testing our code. And that also is easy. with this. So automated tests give you the ability to just set it and forget it. They get run when you run your regular test suite.

13:36

Speaker 1: And if something breaks for some reason then that'll catch it. Test failures can also highlight changes that should be reflected in documentation if you have done any custom documentation. So here's some sample tests. In this case, I'm using the API test case that comes with Django Rest framework. It is built on the Django test test. test case and it's very very similar. You can also use the Django test case directly if you don't want to try to learn something new. There's very little that's different from between this and if you use the Django test case. case directly. So I have a setup function here where I define my URL. I'm doing a reverse on my model. list, and that's going to give me that base URL.

14:22

Speaker 1: This is the namespace that Django Rest framework gives to it , just as if you had put a name equals in a regular URL. And then I'm just gonna create some instances for the purposes of my testing. So I'm just gonna run through and create a couple of instances. And then I've got a test list view here. So now I'm gonna go Do a self. client. get just like I would in regular Django test case. Point at the URL, tell it the format is Django. JSON because this is all JSON. And then I'm gonna do, I'm gonna assert equal that the response status code is a 200 because this is a Git. So I'm expecting, I'm not creating anything. It should just give me a 200 back.

15:07

Speaker 1: And then I'm going to assert that the length of response. data is three. And response. data is one of those little things that you get with the rest framework test case that you don't get directly with the Django test case. That's just going to pull out the data section specifically from the response. So I can just make sure that there are three objects In that response data. You can also check and make sure that the data is looking like you would expect it to look. But I wanted to keep things simple for purposes of this. Couple more samples. Testing creation is very similar. In this case, I'm checking to make sure I get a 201 because it will have created an object. Again, you can also check to make sure that the attributes are what you expect. at this point. And then testing the detail view.

15:54

Speaker 1: I'm showing this primarily because I want to show you how I would affect that URL. So I'm just grabbing any one of the objects I created, just grabbing the first one that comes out of the database at random. And then creating my detail detail URL by slapping that ID onto the end of it. And then I can do the exact same thing. Just do a self dot Client. get at what's now my detail URL, and again make sure that I get a 200 response. All right. So that is it. We have just built an entire API on top of presumably some Django application you already have. So your homework is to think about what Django projects do you have live? What Django projects do you have in development that you could add an API layer to?

16:43

Speaker 1: Do they have public information? That is a fantastic place for an API. Because you'd be amazed what people can pull out of public information. If they have collections of user data, those users might want access to that data in different ways. Think back to that massage therapist example. He wanted access to the data in a way that the original developers didn't anticipate. If you can't think of a use for your data in an API format, don't worry, your users will. And this is going to give you a competitive advantage. Because again, think back to that massage therapist example. If he was using a scheduling software that didn't have an API, but he needed this functionality, here's a motivation for him. him to switch to one that does have an API so he can have this functionality that not many people need, but he needs it enough that he's willing to pay for

17:29

Speaker 1: For a custom software solution. So those little tiny edge cases that you don't want to build out because only two people are ever going to use it Somebody might use it via an API, they'll go to the effort of building it, and then you are have that kind of lock-in going on because you've got that API that makes that possible So at this point, I will take questions. I've got some resources here. Again, as promised, the slide link is in the middle if you want to look that up for reference later. Django Rest Framework documentation, that example project I mentioned, these slides, Cactus Group, and my own Twitter.

18:16

Speaker 2: As I walk to a question, um I'll ask one of my own. I noticed you were talking about the uh the browsability. Is it API-auth? Does that mean that there's authentication implied on top of it?

18:29

Speaker 1: So yeah, you can authenticate um I think that if there's not an authentication layer that you can go directly to the API URLs. I have all of the cases when I've used the browsable API, it's been on one that I did have an authentication layer on it. So I wasn't actually 100% sure on that

18:47

Speaker 3: Yes, thank you. Uh nice talk. I my question is do you version your URL spaces for these exported APIs typically or do you have a use case for that or Any experience with versioning?

19:02

Speaker 1: So you could version if you wanted to. I would probably not recommend it because you're gonna want it to stay current with your app. And so by building it this way, it's just going to continuously track with your app. The only time I would try to version it is if there was a model that I was going to deprecate or something And then by versioning, I could indicate, okay, you know, this particular URL set is going to go away, and then therefore transition. That's the only use case I can think of offhand. When you might want to version it. And then in that case, that ability to register things would give you that ability to transition to a new set of equations. URLs.

19:46

Speaker 4: All right. You talked about um authentication. What about authorization? So if I have access to the get, you know, do a get and I know my user. ID is one, I start poking around and look at two and three and four and five.

20:00

Speaker 1: Yeah. So again, you know, you can look at the logged in user. You know, you have access to request. user just like you would in any other Django request. And you can have a method in that view set that says, you know, if request. user does not equal the request, you know, the user that's being asked for, then permission denied. Um that is very easy to implement using that authorization.

20:28

Speaker 5: Hi. Does Django Rust framework play well with um data sources that aren't the ORM?

20:36

Speaker 1: Oh, that's a good question. I think it would. Assuming you create something to stand in for the serializer , there is plenty of customization that you can do on the serializer, so I would think that that would be how I would do it. Would be to customize the serializer to work with that data source. And then that would pass it into the view set just as if it were any other serialized data. So I think that's how I would do it.

21:03

Speaker 6: Have you ever regretted exposing an API to users because they found something you really didn't want them to find or because they uh um bombarded you with annoying questions

21:14

Speaker 1: I have not ever had that experience. I will say that in every case where authentication mattered for any of the data, I applied the authentication on the API as well. And I think that's just solid, you know , data safety. In general, if it's not public data, I would have that authentication layer. and again it borrows off of the existing Django authentication. If a model is going to throw a 403 when a user requests a particular instance then Django Rest framework will do the same thing as long as you've got the authentication turned on.

21:53

Speaker 7: Hey there. Two uh parts. First a comment then a question. No. Questions on it for the question. Okay, fine. I'm just gonna ignore that. Uh your talk was awesome. Out of all of them, I really look forward to yours and you absolutely killed it. Uh my interest started on my new project. I'm working with an API incorporating with a jQuery data table. Anyway, as for the question, do you know any Django API projects that are using channels? Because I feel they both would work well together. Thanks again.

22:23

Speaker 1: Hmm, so I have not actually worked with channels Myself. It's something that's interesting, but I haven't had a chance to play with it. So I can't really speak to that. I don't think it would work well though because Channels are designed to maintain that open connection, and the API is inherently asynchronous. So I don't think that they would play well

22:50

Speaker 8: Thanks again. Is there any support for the uh is it like GraphXL or some of the query type languages that are built on top of um you know API, the the sort of vanilla API with CRUD. Uh graph Excel or the more advanced querying type API.

23:20

Speaker 1: So anything you can get your application to just Generate, you can then expose through the API. It'll be using a custom serializer generally, again, if it's not directly from a model. But I've done this. I've actually built an API endpoint that gave you generated data. as opposed to the direct original data. So by using that custom serializer functionality, you can get just about anything to go through your API portal.

23:54

Speaker 8: Thanks for the great talk. Do you recommend something like Mixer or Factory Boy in order to To generate better models for your testing?

24:03

Speaker 1: Yes, absolutely. I usually use Factory Boy. I did just direct creation of models in this case, just because it was simpler. But yeah, I usually use Factory Boy to create instances.

24:16

Speaker 2: All right. Thank you very much.

24:18

Speaker 1: Absolutely. Thank you.

Questions this talk answers

What is an API and why is it useful?

An API is code that lets two software programs communicate. It provides flexible access to data and functions, can connect internal systems, and makes it easier to add features or support future needs.

Discussed at 0:14

How can an API add features that an application does not support?

An API can expose existing data in a new way and let a separate script transform or republish it. For example, a scheduling API was used to share appointment times without exposing client names.

Discussed at 1:51

How do I build an API for a Django application with Django REST Framework?

Add a serializer over the Django model, a model viewset that uses the serializer and a queryset, and a router registered in the URL configuration. Django REST Framework then handles the standard API URLs and operations.

Discussed at 4:22

What are serializers, viewsets, and routers in Django REST Framework?

A serializer converts model data to and from the format used by the API, a viewset handles operations such as listing, creating, and updating instances, and a router maps those operations to URLs.

Discussed at 5:08

Which HTTP methods should a CRUD API use?

Use POST to create, GET to read, PUT or PATCH to update, and DELETE to remove data. PUT expects the complete set of fields, while PATCH updates only the fields supplied.

Discussed at 8:59

What URLs does a Django REST Framework CRUD API provide?

A GET request to the collection URL returns a list, and POST to that URL creates an instance. Adding an object ID gives a detail URL, where GET retrieves the object and DELETE removes it.

Discussed at 10:31

How do I restrict access to a Django REST Framework API?

Use authentication based on Django’s user model, or make a viewset read-only. You can also restrict individual actions, such as preventing deletion, and apply authorization rules based on the logged-in user.

Discussed at 11:17

How should I document a Django REST Framework API?

Document each URL and HTTP method, the operation it performs, its expected parameters, and the returned data format. The documentation can be included at a URL from the project’s URL configuration.

Discussed at 12:03

How do I test a Django REST Framework API?

Use REST Framework’s API test case, which works similarly to Django’s regular test case. Set up test data, make client requests, and check status codes and returned data for list, creation, and detail endpoints.

Discussed at 13:36

Should I version Django REST Framework API URLs?

Usually not, because an API built this way can stay current with the application. Versioning is useful when deprecating a model or URL set and providing a transition period to a replacement.

Discussed at 19:02

How do I authorize users so they cannot access another user’s records through the API?

Check the requested user against request.user in the viewset and deny permission when they do not match. Django REST Framework can return a permission error using this authorization logic.

Discussed at 20:00

Can Django REST Framework work with data sources other than the Django ORM?

Yes, by customizing the serializer to work with the alternate data source. The customized serializer can then provide data to the viewset in the same general way as model-backed data.

Discussed at 20:36

Can Django REST Framework expose generated or custom query data?

Yes. Anything the application can generate can be exposed through an API, generally using a custom serializer when the data does not come directly from a model.

Discussed at 23:20

Presenters

Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.

More videos by Charlotte Mays

More videos from DjangoCon US