We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

The Beauty of ViewSets in Django Rest Framework

00:00

Formal Metadata

Title
The Beauty of ViewSets in Django Rest Framework
Title of Series
Part Number
38
Number of Parts
48
Author
Contributors
License
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
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.
6
Thumbnail
42:19
Software frameworkAreaSocial classComputer animation
SoftwareWebsiteSoftware engineeringVideoconferencingCycle (graph theory)GenderTask (computing)Coma BerenicesComputer animation
Fault-tolerant systemExpressionVideo gameCodeView (database)Software frameworkSet (mathematics)Software maintenanceMeeting/InterviewComputer animation
Model theorySingle-precision floating-point formatElectronic mailing listView (database)Uniform resource locatorDigital filterGroup actionImpulse responseDemo (music)Line (geometry)CodeSimilarity (geometry)Pattern languageUniform resource locatorView (database)Pattern languageWeb pageGroup actionSet (mathematics)Declarative programmingCartesian coordinate systemFilter <Stochastik>WebsiteDatabaseSoftware developerDemo (music)Endliche ModelltheorieCodeQuicksortDifferent (Kate Ryan album)Operator (mathematics)Proper mapElectronic mailing listSoftware frameworkField (computer science)Model theorySingle-precision floating-point formatRegular graphSerial portType theoryClient (computing)InternetworkingInformationSynchronizationRoutingDivision (mathematics)Film editingPoint (geometry)Surreal numberLatent heatWhiteboardOrder (biology)Electronic visual displayWave packetFiber (mathematics)Object (grammar)Line (geometry)GenderConnected spaceComputer animation
Demo (music)BitReal numberFunctional (mathematics)Demo (music)Process (computing)Set (mathematics)Flow separationView (database)Software testingComputer animation
Demo (music)Model theoryFile formatSymbol tableMeta elementData modelField (computer science)View (database)Uniform resource locatorFunction (mathematics)WebsiteObject (grammar)Computer clusterParsingDigital filterInclusion mapError messageCellular automatonHidden Markov modelAreaQuery languageManufacturing execution systemView (database)Electronic mailing listLoginRandom matrixDependent and independent variablesSoftware frameworkAuthenticationSerial portPresentation of a groupAsynchronous Transfer ModeFunctional (mathematics)Endliche ModelltheorieInformationParsingFilter <Stochastik>DatabaseQuery languageDecimalKey (cryptography)String (computer science)Field (computer science)Representation (politics)Different (Kate Ryan album)Gastropod shellUniform resource locatorType theoryPoint (geometry)MultiplicationStandard deviationBitCodeMatching (graph theory)Axiom of choiceToken ringSemantics (computer science)Form (programming)Charge carrierModel theoryObject (grammar)Context awarenessSocial classError messageResultantTheory of relativityCASE <Informatik>WordSurreal numberOperator (mathematics)Sinc functionMereologyElectric generatorWeightParameter (computer programming)Medical imagingComputer animation
Social classDigital filterQuery languageObject (grammar)Menu (computing)Model theoryHidden Markov modelMach's principleInterior (topology)Uniform resource locatorWebsiteView (database)Router (computing)Default (computer science)Inclusion mapAuthenticationBoom (sailing)Computer-generated imageryMathematicsGroup actionExecution unitSymbol tableDemo (music)View (database)Front and back endsSelf-organizationSocial classSerial portUniform resource locatorComplete metric spaceCodePhysical systemRoutingQuery languageRegulärer Ausdruck <Textverarbeitung>Line (geometry)InformationDefault (computer science)Object (grammar)Endliche ModelltheorieSoftware frameworkRouter (computing)BitPoint (geometry)Derivation (linguistics)Extension (kinesiology)Set (mathematics)Regular graphRootStandard deviationField (computer science)DatabaseAuthentication2 (number)Multiplication signTwitterInjektivitätPattern languageAttribute grammarMathematicsWeb pagePattern recognitionRight angleTerm (mathematics)Arithmetic progressionFunctional (mathematics)Form (programming)AreaVirtual machineGoodness of fitExecution unitLevel (video gaming)Figurate numberLatent heatParameter (computer programming)Conservation lawMereologyFile formatState of matterDomain nameModel theoryPrice indexFilter <Stochastik>Electronic mailing listComputer animation
Symbol tableModel theoryDigital filterNormed vector spaceWorld Wide Web ConsortiumInclusion mapView (database)Maxima and minimaVoltmeterSummierbarkeitObject (grammar)Process (computing)Software design patternReal numberSoftware testingView (database)Uniform resource locatorCodeSet (mathematics)Electronic mailing listNumberPhysical systemQuery languageTask (computing)Software frameworkControl flowBasis <Mathematik>Instance (computer science)Serial portEuler anglesDatabaseQuicksortRight angleSinc functionCategory of beingAttribute grammarPattern languageTime zoneKey (cryptography)Social classOrder (biology)Software developerParameter (computer programming)Mixed realityRepresentation (politics)InformationMathematicsINTEGRALEndliche ModelltheorieComputer animationLecture/Conference
Coma BerenicesXML
Transcript: English(auto-generated)
OK, 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 Lindsey Jr. I grew up in Oklahoma. And I run a site called gojango.com. I currently have about 150 Django screencasts for people to learn and continue their education on Django.
And I'm continuously adding new videos. So I ask that people check it out. And subscribe if they want to. I would be appreciative of that. Also a senior software engineer at a company in Tulsa called Summit ESP. And I do mostly Django work and a little bit of full stack as well.
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 going to try to learn, because I always buy some new book that she never knows what it's going to be. So with that, this is my wife and my daughter.
My daughter's 10 months old. My wife's Melissa, and our daughter is Grace. And I took this picture on the way up, and my daughter's like, why are you taking pictures of me? That's her expression, I think. So with that, let's get into it. So this is about view sets in Django REST framework. I feel like one of the best places to start
to really understand view sets, because this talk is about maintainability of code, 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 to deal with a single model,
you have to get HTT verbs that you deal with for doing a list of 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 so those are the five verbs that we deal with in dealing with models and the different CRUD
operations. And those are generally made up with two views and two URLs. Your create and list are generally something like API slash coins. It's kind of the base of something. 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 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 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 regular model serializers or a third party type serializer. It's running any filters that you're wanting to do. So you're trying to filter your data with Django filters.
Your views are generally handling that as well. And then they also do the action that we want them to do. It's getting the data to display it. It's deleting it. It's updating or creating a new object in our database. So it's doing a lot of work when we're doing things. And we have to handle all that somehow.
Our URLs are basically doing one thing. They're routing the information that we're sending it to somewhere else. And I have route in quotes because we'll see that here in a little bit. So kind of the one difference that there is with view sets is you're declaring a single URL instead of two.
You have basically one view instead of two or more. And you're only working with code declarations. I'll kind of explain what that is a little more when we actually get to the 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 Django REST framework down to 22 lines, again, including import. So it's taking a lot of code, simplifying it, and making it a lot easier to understand. The good thing about this is you can be able to more quickly grok all of your endpoints.
Where I work at some of the ESP, we have a single page application. And we're converting it from an old code base to a new code base based on Django REST framework. And we have a lot of endpoints. And 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 the exact same thing. And so when you're dealing with a lot of endpoints and it needs to have the robustness to handle at least those two scenarios, you need to be able to understand them really quickly. And then you also need a common pattern
to create all your new endpoints. Because when we add 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 common thing. So with that, let's actually take a look
at a little bit of a demo. And we're going to go on a journey. We're going to go from function-based views. And we're going to look at kind of how everything is going and how everything is done with a function-based view, what it looks like when people convert it to a class-based view, and then finally, what it looks like as a view set.
And you'll see. And then the last thing I'm going to show is how we have developed a process of testing view sets that we feel is a good way to test without over-testing.
That's not that slight.
Everyone see that? OK, 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 cryptocurrency
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.
Let's do it this way. So we have our serializer. It's a standard Django framework model serializer, coin serializer. It's a model serializer. We set our model and the fields that we want to bring in to be 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 one for our ID to get the detail and be able to do a delete.
And then we have our views here. All right, so here's our imports. We'll just ignore those. So the first thing we want to do is, so we'll say we're new to the code base. And we kind of want to understand, what is this doing, because we have this type of code
everywhere for all of our API points. Well, we go to our URLs, we say, OK, API coins, and go to the coin list. Now we're at our coin list view. Well, there's a login that's required. OK, that's good information. CSRF exempt. OK, that's good information. Now we know that we don't have to have a CSRF token.
I'm doing a post. Then the first thing we look for, OK, 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 last filter that we applied with the query set. We're setting the many equal to true.
OK, I mean, if we're used to this, 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. OK, now we're doing a post. We're checking for the post. We're doing a parse of the data,
serializing it, checking the wallet, and then saving. OK, now we have that. Let's go on to the next one. Oh, we're doing a login required again. We're doing a CSRF exempt again. Getting our details. We're passing in the request on the primary key. Now we're getting the data out of the database. If it doesn't exist, we're 404ing, OK? We're getting our get, serialize, return response.
And now we're checking for put. Parsing, serialize, is valid, save. Returning our response either with data or a 400 status. There's an error. And finally, there's a delete. We're deleting the data out of the database and returning that it was deleted.
Very common function-based views. There's a lot of code to it. And if you want to do anything custom in there, it's going to make this longer and more complicated. And when you have a lot of code like this, at some point, you're going to wonder, well, where do I put custom information when dealing with my actual model data
and how I specifically save models? So one scenario might be is you're doing a many-to-many object, and you're passing in, 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 that stuff 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 do some poor design choices.
This is still valid and still works. And at the end of the day, it's going to be fine as long as you understand the caveats to it. Let's quickly look at our Django filters. We're just doing a simple filter for the coin.
So they're just doing a case-insensitive exact match for the name. 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 move on to look at our class-based views.
So our URLs have slightly changed a bit. We're doing our 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 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 serializer as the top-level serializer. Again, as I was describing here a minute ago, if 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 in your views here because your serializer will just, quote unquote, magically handle it the way you want it to handle it. By default, Django REST 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 with 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 if we want to filter individual fields. And we can do some other custom things
that are kind of like the primary filter of this view. So coin detail view is very similar. We're doing retrieve, update, destroy API view. So we've now determined in one line that this view will get the data, update the data, and destroy the data for a specific object in the database or model in the database.
That's going to be authenticated. It's going to use the coin serializer as a true coin or as its root serializer. And it's going to, again, start the filtering based on the coin object. So 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 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 the views in the first place,
because we're kind of declaring these things are going to happen in our class-based views. And these are the attributes that need to be here. And we don't necessarily want to do anything else. This is what we want. So 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. 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 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 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 all is going on. We've kind of seen a progression. The long-form function-based view set
that's only a few lines of code. And we quickly understand and quickly see what's going on. And in reality, let's say I need 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 a 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. 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 would need that. So basically, you instantiate a new router.
You register a URL point of, say, coins in this place because we're doing API slash 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's going to figure out, hey, is there 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 they're based on the HTTP verb. So it's a very smart little system. And then down here in the patterns,
we have just pretended it with an API, and we're doing an include with router.urls. So it uses a standard Django URL pattern recognition, regular expression system, as it tries to figure out what it needs to do. So with that in mind,
we've written basically very little code at this point to get something to work. Let's actually make it do something. So if we refresh the page, authentication credentials are required. We log in.
Now we're logged in. So if I refresh the page, we're getting our bit coins. This has the bit filter on it. So if I comment that out,
refresh, now I have the five bit coins that were in my database. Go in here, and I can also, it does 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 of it
so that it returns back. And we've gotten a lot of that without a 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 allow opportunity for Q and A. So, one second.
If you wanna learn more, go to gojango.com, or you can hit me up on Twitter, I have Buddy Lindsey as my Twitter handle,
and then you can go to my GitHub, and I'll put this code up on GitHub later today after this talk so you can experiment with it. I have 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, so this is a test of our view set. One thing that a coworker pointed out to me, a coworker kind of designed this pattern,
and he's a super smart developer, 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 needed conclusions, and I love this, and 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 setting up a view set test, 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. Basically, we're checking all the attributes
that are in this view set, and if you might have noticed, I don't have the query set set. That's because I've figured out a way to properly test that and get some sort of assertion to work. Like, it kind of just works, and I'm like, this is good enough, hopefully it never changes, it shouldn't. 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, we're considering these attribution, these properties, these attributes, whatever you're gonna call them, as code themselves instead of just properties on a 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 set will always work the way the view set should work, so we don't see the need to go ahead and do full integration tests, to test every single scenario that views would generally do stuff.
So with that, let's open it up to Q&A. I don't have any questions. We have five minutes left or so. How would you go about sending an array of numbers in a get pattern using this pattern?
So that would just be a normal, so you're talking like how would I post an array of- No, actually, how would you do the get? I'm having an issue with that. I don't want to break the URL in a view set. How would I send it an array of numbers in the get so we'll do a process?
Yes? No, I want to send a bunch of, an array of numbers so on the get, you'll do a, let's say a selecting. Okay, so, okay, I mean- How would I do that in a view set? 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 view sets and so you would have to do something a little more custom with view. 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 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 view set, but I would generally probably create a class-based view for that specific thing
and then I would actually remove the specific mix in. If everything else is gonna be the same, I would create, do the URL, remove the mix in for that particular thing and so that way it doesn't activate that for that set. Thank you. When you're not implementing every method on a particular view set, do you assert
that a method isn't present in any way? So, let me rephrase it, make sure I understand what you're saying. If, so, if I don't want to use a get and I don't have, say, the, 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, that's kind of how that works, kind of automatic switching on itself. That's kind of some of the base,
class-based view stuff in Django as long as you don't have that method on that view, then that's not going to be available and it's going to throw an error. 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 look at it as we don't want to test the framework, we want to test our code. Does that make sense? It sounds like that is the end of our Q and A.
Let's thank Buddy for this wonderful information.