Django Authors Panel
Published November 3, 2017
This video features Buddy Lindsey Jr. at DjangoCon US 2017 in Spokane, Washington, USA.
DjangoCon US 2017 - The Beauty of ViewSets in Django Rest Framework by Buddy Lindsey Jr.
ViewSets will make your code shorter, more robust, and save you time during your development, if you let them. I have spent a lot of time dealing with writing view code, and dealing with all the urls, only to finally learn ViewSets. It immediately saved development time as well as making my code more simple.
Generally to make a new, basic, endpoint in DRF for a model it would take about 15 minutes. That includes creating a serializer, urls, views, and testing it the browser. Now that same endpoint is more easily understood and done, all the steps, in less than 5 minutes. Leaving you more time to worry about what your new app is supposed to actually do.
This talk was presented at: https://2017.djangocon.us/talks/the-beauty-of-viewsets-in-django-rest-framework/
LINKS:
Follow Buddy Lindsey Jr. 👇
On Twitter: https://twitter.com/buddylindsey
Official homepage: https://godjango.com
Follow DjangCon US 👇
https://twitter.com/djangocon
Follow DEFNA 👇
https://twitter.com/defnado
https://www.defna.org/
Buddy Lindsey Jr. explains how Django REST Framework ViewSets consolidate the list, create, retrieve, update, and delete operations for a model into one concise, declarative class. He compares function-based views, class-based generic views, and ViewSets, showing how serializers should handle nested or many-to-many data while filters, permissions, querysets, and serializers remain explicit on the view. Routers then map one registered URL to the appropriate action based on the URL and HTTP verb. He also presents a testing pattern that asserts a ViewSet’s configured attributes without duplicating DRF’s own framework tests, and notes that unusual query-parameter processing may be better handled by a custom class-based view or an overridden ViewSet action.
Summarised automatically from the transcript.
Automatically transcribed, so expect mistakes in names and technical terms.
Speaker 1: Thank you. Thank you for coming. Can everyone hear me okay? Just checking. Awesome. Okay, so based on how fast I talk during my lightning talk, we could be only here for about five minutes. So we'll see because I was really going for some reason. Anyway so first up who am I? I'm Buddy Lindsay Jr. I grew up in Oklahoma and uh I run a site called gojango. com I currently have about 150 uh Django screencasts for people to learn and continue their education on uh Django and I'm uh continuously adding new videos so I I uh ask that people check it out and uh subscribe if they want to. Um I would be appreciative of that. I'm also a senior software engineer at a uh a company in Tulsa called Summit DSP and I do uh mostly uh Django work and bit of full stack as well.
Speaker 1: And I'm also a lifelong learner. The two latest things I've been learning about is sustainable farming and permaculture and also cryptocurrency trading. My wife has constantly said She's always curious every six months about what new thing I'm gonna try to learn because I always buy some new book that she never knows what it's gonna be. So uh with that this is my wife and my daughters. My daughter's ten months old, my wife's Melissa and our daughter is Grace And uh I took this picture on the way up and my daughter's like, why are you taking pictures of me? That that's her expression I think. So with that, let's get into it. Uh so this is about viewsets and uh in Django Rest framework. I feel like the one of the best places to start to really understand uh view sets because this talk is about uh
Speaker 1: maintainability of code, um aesthetics of code, and being able to read the code really fast and quickly grok and understand what's going on. We'll start at the beginning where you have the anatomy of an endpoint for a single model. Generally you have uh to deal with a single model you have two get uh hd T verbs that you deal with for doing a list of all everything and the details of a specific object out of the database. You have a post for create and you have a put for update and delete for deleting. And uh so kind of those are the the five verbs that we deal with um in doing uh dealing with models and uh uh the different crud operations and those are gener those are generally made up of uh two views and two urls. Uh your
Speaker 1: create and list are generally something like API slash coins. It's kind of the base of something and And then your detail view, your update, and your delete are say API coins and the ID of whatever that object is. So those are kind of some of the basics. Everyone is aware of that. I like to start from the beginning to know where we're going. So our views are generally doing a few things. They're determining what verb is being used and how to route that properly. It's checking permissions if there's any permissions. permissions to be set. It's getting the relevant data that you're after to be able to display it to the user. It's generally serializing the data using some sort of serializer. Using the uh regular model serializers or a third-party type serializer.
Speaker 1: It's running any filters that you're wanting to do. So you're trying to filter your data with Django filters. Uh your views are generally handling that as well. And then they also do the action that we want them to do. It's either getting the data to display it, it's deleting it, it's updating or creating a new a new object in our database. So it's doing a lot of work. when we're when we're doing things, uh it's just and we have to handle all that somehow. Uh our URLs are basically doing one ti one thing. They're routing um the information that we're sending it to somewhere else. And I have route and quote because we'll uh see that here in a little bit. So kind of the one difference that there is with uh view sets is you're declaring a single URL instead of two. You have basically one view
Speaker 1: uh instead of two or two and more and uh you're only working with code declarations and I'll kind of explain what that is a little more uh when we actually get to the kind of demo and I can kind of show you what's going on And then again in the demo, we're going to take 59 lines of code, including our imports, of using function-based views with uh uh uh Django REST framework down to 22 lines again including imports. So it's kind of it's taking a lot of code, simplifying it, and making it a lot easier to lot easier to understand. understand. The good thing about this is you can be able to more quickly grok all of your endpoints. Where I work at Summit ESP, we have a single page application. And we're converting it from an old
Speaker 1: code base to a new code base based on Django Roth framework. And we have a lot of endpoints. And we're going to We're constantly adding more. And we're not only backing our single page application, we're also backing an offline client that we have as well that we wrote. So people out in the field doing work can sync down information onto their local computer do stuff without having an internet connection and then can push that information back up at a later date. And those are hitting the exact same endpoints that we have. For our single-page application. So they're being heavily used and in multiple ways to accomplish kind of the exact same thing. And so when you're dealing with a lot of endpoints and it needs to have the robustness to handle um at least those two scenarios, uh
Speaker 1: you need to be able to understand them really quickly. And then you also need a common pattern to create all your new uh uh your new endpoints because when we add uh a new page to our site and to the offline client we can generally create anywhere from one to five or six different endpoints and we have a lot more stuff in our roadmap to add to the site. And so it's something that we really need to make sure that we get well, we understand it well, and we have a pattern for not only us to follow in the future, but for our future developers and future team members to be able to come on board, see what's going on, and kind of replicate exactly what we've done and understand a cost Common uh common thing. So with that, let's actually take a look at a little bit of a demo.
Speaker 1: And we're gonna go on a journey. We're gonna go from uh function-based views and we're gonna look at kind of how everything has gone And how everything is done with a function-based view, uh, what it looks like when people convert it to a class-based view, and then finally what it looks like as a uh View set, sorry. You'll see. And then the last thing I'm going to show is how we have developed the process of testing viewsets. Um that we feel is a a good way to test without over-testing. And that's not that slight. Okay, so So let me see if I can do this.
Speaker 1: Everyone see that? Okay, cool. So I'm using presentation mode in PyCharm. Never used it before until today, so we'll see how well this goes. So kind of starting from the beginning, we have our model. It's a very basic Django model. Name, symbol, price in US dollars, price based against the Bitcoin for doing a crypto currency coin, character field, decimal fields, and then we have our string representation. So when we look at it in the shell or the admin we know what it looks like. Very very basic. Okay. Might not work out too well.
Speaker 1: Let's do it this way. So we have our serializer. It's a standard Django model ser Django REST Framework model serializer. Corn serial coin serializer. It's a model serializer. We set our model and the fields that we want to bring in to be a able to display to our users. And then in this case we have our URLs. We're pulling in two function-based views We're setting it to our first URL to be able to get our list and to be able to create our coin object and we have our one for our ID to get the detail and be able to do a delete. and update. Uh and then we have our views here. Okay Alright, so here's our imports. We'll just ignore those.
Speaker 1: So the first thing we want to do is so let's say we're new to the code base and we kind of want to understand you know, what is this doing? Because we have the we have this type of code everywhere for all of our API points. Well we go to our URLs, we say okay, API coins, and go to the coin list. Now we're at our coin list view. Well, there's login that's required. Okay, that's good information. CSRF exempt. Okay, that's good information. Now we know that we don't have to have a CFR CSRF token on doing a post. Then then we first thing we look okay we're doing a get, we're getting our data, we're applying all the filters that we might want to apply. And then we're serializing the data based on the
Speaker 1: last uh filter that we applied with the Q query set. We're setting the many to true. Okay, I mean if we're used to this, you know, we're getting a lot of information fairly quickly, but if we're not that used to it, we're probably having to look up a lot of different things to try to really understand what's going on. So when they were returning a JSON response with that data. Okay, now we're doing a post. We're checking for the post. We're doing a parse of the data, serializing it, checking if it's valid, and then saving. Okay, now we have that. Let's go on to the next one. Oh, we're doing our login required again. We're doing a CSRF exempt again. We're getting our details. We're passing in the request on the primary key. Now we're getting the data out of the database. It doesn't exist. We're 404-ing, okay. We're getting our get, serialized, return response.
Speaker 1: And now we're checking for put. Parsing, serialize, isvalid, save, returning a response either with data or a 400 status. There's an error. And finally, if there's a delete, we're deleting the data out of the database and returning that it was deleted. Very common function-based views. But there's a there's a lot of code to it. And if you have to want to do anything custom in there, it's gonna make this longer and more complicated. And when you have a lot of code like this, at some point you're gonna wonder like, well, where do I put custom information when dealing with uh my actual model data and and how I specifically save models. So one of those one scenario might be is you're doing a many to many object and you're passing in
Speaker 1: uh in your JSON request subdata that needs to create new objects. Well when you have a lot of information in here you might be tempted to try to create all of those objects and do all of that stuff. um inside of your view. When in reality the best place to actually do that is in your serializer because you might use that same serializer in multiple different views and you want to handle that chunk of JSON inside of your serializer somewhere else. So this leaves you an opportunity to uh do some uh poor design choices. Uh this is still valid and still works and at the end of the day uh is gonna be is gonna be fine as long as you understand the caveats too. to it. Uh let's quickly looking at our at our uh
Speaker 1: Django filters. We're just doing a simple like filter for the coin. So it's doing a uh basically uh case insensitive exact uh match for the name. Uh anyway. Just fairly basic stuff. And then in this one we're doing a bit filter in case for some reason we wanted to filter out every single result. unless the name of it had bit in it. This was just something arbitrary I came up with just to show an example. So let's uh move on to look at our class-based views Okay.
Speaker 1: So our URLs have slightly changed a bit. We're doing our corn coin list view and coin detail view. We're doing our as view. Here because they're class-based views, we're not calling the functions directly. So in our views, we've actually slimmed it up a lot. We have a coin list coin list view, it's inheriting from list create API view from Django Rest framework Now we see we have permission classes and we're checking for authentication. Awesome. We're setting our serializer. So now we know that this is going to serialize anything that comes in based on the coin serializer as the top-level serializer. Again, as I was describing here a minute ago, if uh you're dealing with many-to-many data or related data and you handle that in your serializer, you don't have to override anything.
Speaker 1: in your views here because your serializer will just kind of quote unquote magically handle it uh the way you want it to handle it. By default uh Django framework does not handle that and you have to handle that somewhere yourself That could be a completely different discussion for another presentation because there are some headaches involved in that. So there's also the filters that I was just showing you. You generally start with a Django filter backend. And here we just added our bit filter as an example. We have our query set in here so we know hey this view is going to start with this query set. And everything that it does beyond that is going to be a derivative of this query set. And then finally we have the filter class, which is the coin filter, and that's kind of the main filter for
Speaker 1: we want to filter individual fields and you know we can do some other custom things that are kind of like a the primary filter of of this uh view So coin detail view is very similar. We're doing retrieve, update, destroy API view. So we've now determined that in one line that this view will get the data, update the data, and destroy the data for a specific uh object in the database or uh model in the database. It's going to be authenticated, it's going to use the coin serializer as its root coin or as its root serializer and it's going to again start the filtering based on the coin object So now that now if we compare our class-based views with our function-based views, we've gotten the exact same information from reading this that we did
Speaker 1: in in the function based views as we did with the class based views, we just did it a lot faster. And it makes more sense once we understand how class-based views work. And we don't necessarily have the opportunity to inject code that doesn't need to be in uh the views in the first place because we're kind of declaring these things are gonna happen in our class-based views and these are the attributes that need to be here uh and we don't necessarily want to do anything else. This is what we want. So, and this is why I'm a proponent of class-based views in general, because it can lead to drier code and better organization of the code. So finally let's jump to our view sets.
Speaker 1: Alright, so here we go. We have our imports at the top, and now we have our coin view set, and we only have one view. And so that's why it's a view set, it's combining multiple views views into one. And we can quickly tell we're doing a create, we're doing a list, we're doing an update, we're doing a retrieve, we're doing a destroy, and everything is being based on a generic view set. We've now quickly determined exactly what this view is going to do. Again we have the permission classes. We have a coin serializer. that the root view is going to work on. We have our two filters, query set, and our main filter class. So now we've even now more quickly determined what it what all is going on. We've kind of seen a progression, the long form function-based views to this
Speaker 1: view set. That's only a few lines of code, and you quickly understand and quickly see what's going on. And in reality, uh let's say I needed to do like a market data view set, I would copy and paste this. chunk of code and then change all the information and now I have my complete view system in the views file done for the market data. The only thing I have to do now is go in and add URLs So that's where things get a little bit tricky compared to regular views in Django is you have to deal with a router inside of Django REST framework. and being able to route information to different places properly. Generally you can survive with using just the default router. I have only ever written a custom router for the exercise of writing a custom router to understand what it does.
Speaker 1: I have not needed to do one in practice. If anyone has, please let me know because I'd love to have a conversation and understand that piece better of why someone made the that. But basically you instantiate a new router, you uh register a uh URL point of say coins in this place because we're doing API slash coins coins and you set the view set that you want to use and so now whatever you have you're going to do coins slash And it's going to do everything else. That router is going to figure out, hey, is there a uh an extension on that URL for a primary key? Yes, okay. Now I need to go to say that second class-based view in a sense To either do an update or either do a get the details, do an update or do a delete, and then it determines there
Speaker 1: based on the HTTP verb. So it's a very smart little system. And then down here in the patterns, we have just prepended it with an API, and we're doing an include with router. URLs. So use a standard Django URLs pattern recognition regular expression system. uh as it tries to figure out what it needs to do. So uh with that in mind uh We've written basically very little code at this point on to get something to work. Let's actually make it make some make it do something. So if we refresh the page, authentication credentials are required. So we log
Speaker 1: in. So now we're logged in. So if I refresh the page We're getting all of our uh we're getting our uh bitcoins this has the uh bit filter on it so if I uh cut comment that out Refresh. Now I have the five bitcoins that were in my database. Go in here and I can also do the filtering And it also gets my details. And if it doesn't have anything, it returns a not found. So kind of our 404 system that we had in place that it so it that it returns back. And we've gotten a lot of that without a
Speaker 1: lot of extra effort and a lot slimmer code and a lot more readable code. So anyway, with that, I think I ended about the time that I wanted to to uh allow opportunity for Q<unk>A. So one second. If you want to learn more, you can go to gojjango. com and uh or you can uh hit me up on Twitter uh at BuddyLindsay is my uh Twitter handle and then uh you can go to my GitHub and I'll put this code up on GitHub later today after this talk so you can uh experiment with it. I have
Speaker 1: uh Tags for the different ones so you can kind of check them out. I also just realized I didn't show you one thing I wanted to show you. Okay. Okay, so this is a test of our view this test of our view set. Uh one thing that um But a coworker pointed out to my
Speaker 1: I a coworker kind of designed this pattern and uh he's super smart developer in my in my opinion And I enjoy learning from him. And this is kind of his pattern and we kind of discussed it and came up with kind of these conclusions. And and I love this and I I think it makes a lot of sense. So in order for this view set to be fully tested, we have this test system in place. We're we're setting up a view set test as our we're instantiating the new view set and then we're checking to make sure all of the instances that we're expecting are set on that instantiated object. We're also checking if the permission classes are set that we expect as well. And we're setting the serializer class. Again, I mean basically we're checking all the attributes that are in this view set.
Speaker 1: And if you're if you might have noticed I don't have the query set set. That's because I haven't figured out a way to properly test that and get some sort of assertion to work. like kind of just works and I'm like this is good enough hopefully it never changes it shouldn't uh and that's the thing is this is so bare bones enough that your view sets should very rarely ever change And the reason we do tests like this is because in this case, uh, we're considering these attributions, these properties, these attributes, whatever you want to call them, uh as code themselves instead of just properties on a client. class and we don't want these to change and if these do ever change we want to be notified that they've changed. We're trusting that the view
Speaker 1: set will always work the way the view set should work. So we don't uh see the need to go go ahead and do full integration test to test every single scenario that views would uh generally do still. So with that is uh let's open up to QA. Um anyone have any questions?
Speaker 2: How would you go about uh sending uh an array of uh of numbers in a get pattern using this pattern.
Speaker 1: So that would just be a normal uh So you're talking like how would I post an array of
Speaker 2: No actually how would you do the get? I'm having an issue with that. I don't want to break the URL uh in a view set. How would I Send it an array of numbers in in the get so we'll do a process.
Speaker 1: I guess I'm
Speaker 2: Yes. No, I want to send uh a bunch of an array of numbers, so on the get you'll do uh let's say a selecting.
Speaker 1: Okay, so okay. I mean
Speaker 2: how would I do that in a view set?
Speaker 1: So you would have so you basically have a query parameter in the URL that has some set of numbers and then you would need to process that accordingly. That would generally kind of break outside of of of U sets, and so you would have to do something a little more custom with U setsets. your uh with your view at that point. Uh this is kind of tightly defined to I am sending JSON data of an object that I want to save to the database or getting out a JSON representation. representation of the object in the database. We need to do custom processing like that. You can jump down into and override stuff inside of the viewset , but I generally probably create a class-based view for that specific specific thing and then I would actually remove the specific mix
Speaker 1: in. If everything else is going to be the same, I would create do do the URL, remove the mix in for that particular thing. And uh uh so that way it doesn't activate that verb for that view set.
Speaker 2: Thank you.
Speaker 3: When you're not implementing every method on a particular view set Do you assert that uh method isn't present in any way?
Speaker 1: So let me rephrase it, make sure I understand what you're saying. Uh if So if I don't want to use a get and I don't have say the uh if I don't want to use a get to get a list I wouldn't have this list model in here. That would mean that the get for with no primary key, it would not activate that HTTP verb and it would return a 404 Yeah, so yeah, uh that that that's kind of how that we're kind of automatic switching on itself. That's kind of some of the base uh class-based view stuff in Django, as long as you if you don't have that method on that um view, then that's not gonna be available and it's gonna throw an error to the user.
Speaker 1: I since to me, since that's a feature of the framework itself, it doesn't need to be tested. Because they have tests, they should have tests for that. And I think they do. So that's something that I don't need to worry about testing Because it's kind of we we look at it as we don't want to test the framework, we want to test our code. It's kind of Does that make sense?
Speaker 4: It sounds like that is the end of our Q<unk>A. Let's thank buddy for this wonderful information.
A ViewSet combines the list, create, retrieve, update, and destroy behavior into one compact declaration. This reduces duplicated code and makes an API endpoint faster to understand and easier to reproduce for new endpoints.
Discussed at 16:14Instantiate a router, register the URL prefix and ViewSet, and include the router’s URLs under an API prefix. The router handles dispatching collection and detail URLs based on the URL’s primary key and HTTP verb.
Discussed at 17:49Test the ViewSet’s configuration directly: verify its actions, permission classes, and serializer class are set to the expected values. The speaker treats these declarations as application code, while relying on Django REST Framework’s own tests for framework behavior and avoiding full integration tests for every scenario.
Discussed at 21:37Put the values in a query parameter and process them with custom logic. Since this falls outside the ViewSet’s usual object-in/object-out pattern, the speaker recommends using a custom class-based view or overriding the relevant ViewSet behavior and removing the unused mixin if necessary.
Discussed at 24:18If the corresponding action or mixin is not included, that HTTP operation is not activated and the request returns an error, such as a 404. The speaker does not separately test this because it is behavior provided by the framework rather than by the application code.
Discussed at 25:26Note: 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.
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 15, 2026
Published July 14, 2026