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

Mighty Model Managers

00:00

Formal Metadata

Title
Mighty Model Managers
Title of Series
Part Number
6
Number of Parts
52
Author
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
Model Managers are an amazing part of the Django Framework. When put to use, they can bring enhanced readability, encapsulation of logic, increased security, and performance. But they're often overlooked - even by those with years of experience. Let's fix that. We'll go through examples that demonstrate how easy Model Managers are to integrate into a project and why they're so important.
13
Thumbnail
42:32
Bit rateModel theoryMereologyData managementCore dumpComputer animation
Process (computing)MetreComplex (psychology)Natural numberCartesian coordinate systemSoftwareRight angleCASE <Informatik>Data managementMultiplication signExtension (kinesiology)Lecture/ConferenceComputer animation
FamilyComplex (psychology)Video gameCodeQuery languageData managementMathematical analysisResultantBitSampling (statistics)TrailCartesian coordinate systemText editorSelf-organizationTraffic reportingLink (knot theory)Right angleReal numberSystem administratorSemantics (computer science)Object (grammar)Multiplication signDefault (computer science)CuboidModel theoryLine (geometry)Process (computing)Electronic mailing listProjective planeReading (process)MathematicsNumberGoodness of fitWordInstance (computer science)Stability theoryFunctional (mathematics)Level (video gaming)EmailInternet service providerEncapsulation (object-oriented programming)LogicCore dumpSoftware frameworkMoment (mathematics)Symbol tableNear-ringMereologyView (database)Table (information)Message passingAuthorizationUsabilitySoftware maintenanceSystem callDemosceneComputer animationLecture/Conference
Data managementText editorAuthorizationQuery languageOperator (mathematics)Interface (computing)Model theoryMobile appDatabaseType theoryExistenceRight angleBitDisk read-and-write headCodeSet (mathematics)Process (computing)Cartesian coordinate systemLimit (category theory)Entire functionMultiplication signDifferent (Kate Ryan album)InjektivitätMereologyResultantInstance (computer science)Point (geometry)View (database)Fundamental theorem of algebraInformation securityHand fanArithmetic meanWeightField (computer science)EstimatorOffice suiteSystem callLecture/ConferenceMeeting/InterviewComputer animation
Object (grammar)WritingBitQuery languageComplex (psychology)Different (Kate Ryan album)Right angleRaw image formatDefault (computer science)Cartesian coordinate systemSet (mathematics)Multiplication signSoftware frameworkRow (database)Game controllerModel theoryData managementCore dumpAttribute grammarCASE <Informatik>Software testingComputer configurationInstance (computer science)DatabaseCodeBoolean algebraTouch typingPoint (geometry)Field (computer science)Encapsulation (object-oriented programming)ChainSystem callResponse time (technology)Charge carrierWordMereologyPhysical systemReal numberNominal numberFilter <Stochastik>Graphics tabletQuicksortComputer animationLecture/Conference
Order (biology)NumberInfinityInformationQuery languageOptical disc driveMultiplication signSpecial unitary groupCASE <Informatik>Row (database)Disk read-and-write headDifferent (Kate Ryan album)Filter <Stochastik>Cache (computing)Set (mathematics)Model theoryData managementContext awarenessBitExistenceObject (grammar)Right angleCategory of beingUser profileInstance (computer science)ArmSocial classWeb pageReal numberDefault (computer science)Parameter (computer programming)Goodness of fitSound effectHypermediaWordFourier seriesObject-relational mappingTheory of relativityQuicksortMixed realityRoundness (object)QuarkMeeting/Interview
Software testingComputer animationXML
Transcript: English(auto-generated)
All right, so thanks everybody for coming out. I really appreciate it. This talk is called Mighty Model Managers, as he said.
So first of all, I have to give you all props. It's pretty full in here. It's 4.40 and it's beautiful outside and you're here to talk with me about part of Django core that's been around for years. So you are the true believers. So give yourselves a hand. So we're gonna be talking about model managers, specifically creating model manager subclasses
and how that makes our lives much easier. And it really does, so get excited. So first off, why are we here? Not like in an existential sense. I mean like why are we here as makers of software? What are our jobs? And for me, I think that really making software is primarily about the process of managing complexity.
And so depending on the case, sometimes we're managing complexity on behalf of our users or the complexity in a business process. But a lot of times we gotta take a step back and make sure we're doing it right and that we're managing complexity for ourselves in the applications that we make. And we can probably all agree on that, at least to some extent. So why listen to me talk about managing complexity?
What do I know, right? Well, this is my family. So my wife and I have four amazing kids. They're ages six and under. I love it and I love them very much, but I learned a lot about managing complexity along the way. Also, I work for a company called eFella. We work in healthcare, which has also
got its own complexity, but that's not really as cute as a picture of a bunch of kids, right? So, oh, so why do I wanna talk about model managers? Well, the thing is they're part of the core framework. The documentation is spot on, they're really useful, and I feel like they're overlooked in the community. I have a bunch of friends and we talk
and I feel like it solves a number of problems where people really aren't looking at them. I think it's one of those things. You just get it working and sometimes you step away. So my goal is that we can talk about this stuff today and you'll be inclined to take at least one thing away from you to implement in one of your applications. We're gonna get there, I promise. So there are a ton of benefits to using managers.
There's improved semantics, readability, encapsulation of logic, ease of maintenance. There's a lot more, but this is a short talk, so we'll keep it quick. So show of hands, how many here in attendance use model managers in your code today?
Okay, so a whole bunch of you, that's great. So for anyone who didn't raise your hand, there's a little bit of a trick question, so sorry at your expense, but if you use the ORM at all you've used model managers in one way or another. So this simple line right here, animal.objects.all. This call to objects, objects is the default model manager that Django gives you right out of the box.
So it's right here is providing us a query set back and we're saying give me all. So I'm getting all instances of my animal model. Pretty simple. If you wanna see it a little more explicitly, you can actually code it right out. This is what's happening for you behind the scenes. So when you define animal, you could put objects equals models.manager.
You wouldn't accomplish anything new, but it would be really explicit. So what's the job of a manager? Is that the only job is to give us a query set? Not really. So the first job of a model manager is to control the query set that you get back when you want to inspect it. But aside from that, the second job is to provide table level functionality for the model.
So if you think of it this way, if you wanted to implement functionality on an instance of a model, you'd create a method and you'd put it on that model. If you wanted to provide table level functionality or something that operates against all the instances of a model, you would create it on that model's manager instead.
So as is usually the case, if you start tweaking things a little bit, you can get some really interesting results. So to demonstrate that, let's talk a little bit about a sample project. So we get together with our team and we decide we need an application for keeping track of the people within our organization. It's really simple. It only needs to remember two things about a person,
their name and their role within the company. So you say, no big deal. You're either an author or an editor, sorry. So you say, no big deal. You head back, you code up this model right here. Everything is good to go. You toss it into the admin and now we can start tracking these things.
To query that model, real simple like we talked about, just person.objects.all and then you get your results set. But the semantics of this are a little weird. We were all used to seeing model.objects.all all the time. It's commonplace to us, but people aren't objects. People are people. So if we wanna make that slightly more semantic,
it's a really simple change. All you have to do is, as we said before, you can still use the default manager that comes right out of the box with Django. Just rename it. So from now on, if I wanna query in a more semantic way, taking my list of person objects, I say person.people.all. Again, same result, just a little bit more semantic along the way.
This is a small change, but honestly, readability is important. And I think this community gets that in general. We have a lot of emphasis on code quality with PEP8 and a number of other things. You don't have to take my word for it. Ask Trey. Trey did a whole talk about this yesterday. I'm sure he'll be happy to tell you. Maybe he's here. So another thing, you may be thinking,
is this really a matter? And it does, and I think that I can convince you. You probably wouldn't leave your model looking like this in the Django admin. Persons is not a great way of representing my instances of person. You would go to your model, you would add the verbose name for it, and it would be right. You do it that way. The thing is, just because the end user isn't seeing the change
or the way that we query our models doesn't mean that that's not important. So we talked a lot about semantics and some fuzzy stuff, and not everybody may be convinced that it's worth it. But there's some much more concrete benefits to be had. The encapsulation of business logic is really important,
especially as your application starts to grow. So say someone comes to you and they ask, I need to get a list of all of our editors. How can I do that? So you say, well, go ahead. Person.people.filter role equals e. There's all of our editors, no problem. The issue is that that query, most likely, is gonna be replicated throughout your view code
all over the application. If you need to create a report of all the editors, you're gonna write that query. If you need to send a mail message to all of your editors, you're probably gonna write that query. It's gonna be sprinkled throughout. And it works, but you kinda wonder, is this the right way to do it? Bernie does not think it's the right way to do it.
So we can do a lot better than that. So what we'll do is we'll set up a manager subclass specifically for editors. So there's two quick steps to that. First, we'll subclass manager, and the only thing you need to do is provide a getQuerySet method. So in here, you can see we're putting the filter right inside the manager, so this is all wrapped up.
Then we just have to reference that manager in our model. So we can leave the existing manager intact, and we'll set up a reference called editors to our new manager. So now the query process is a little bit different, but it's really clean, again, readability. So person.editors.all is pretty slick.
It's very obvious to anyone who's writing the code, new members to the team. Everyone's gonna understand exactly what's going on with that. So what if we wanted to say, I need to replicate that process for authors. Editor's not the only person that I care about, and it's really straightforward. So I'll create another manager, or I'll call it author manager.
Reference that in my model just like this, and it can live right alongside the other two. There's really no limit to the amount of managers that you can have. Again, same process to query, person.authors.all. So this is good, this is familiar. Everyone's gonna get this who's using your application. There's some drawbacks, though, to this approach.
So because we are getting a query set back, when we say person.editors.all, you're getting a raw query, a Django query set, or a base query set. So there's no customer operations. You don't have any way to inject custom code there. The other thing is we're creating a subclass every time we wanna have one of these little handy shortcuts, and it's gonna start feeling pretty heavy after a while,
especially if you need to do an intermediary step for all of those. So it'll work for now, but there's probably a way that we can make this better. There is. So what we can do is we can instead subclass query set. So now I have two parts of this process. I have a person query set, and I'm just gonna expose two methods, authors and editors,
and that's where I'm gonna do my filter. The query set is filtering itself on the role. I'm gonna create a new manager called person manager, and I'm just gonna set up pass-through methods to those two. I'm gonna have authors and editors, and all it's doing is calling, it's calling getQuerySet.authors.editor.
I can throw away my other editors now. This is gonna replace them. So our model now, it's gonna look like this. We're gonna have, and our person, there's only one manager, it's called people, and it points directly to person manager. So now, this is what my query interface looks like. It's slightly different, but it's getting better.
So I have the ability to say person.people.editors. I get my same results as before. Person.people.authors, same results as before. And since now I've subclassed query set, my custom operations are now here, so I can chain these together. I couldn't do that before. So I could say person.people.authors.editors,
which that doesn't make sense yet, because you can only be one or the other, but we'll show how that comes into play in just a few. So now the team gets back together, and they say, the app is great, but we need to be able to track whether a person is full-time or part-time. No worries, we're just gonna update the model, so we add that employee type attribute,
either full-time or part-time. No worries. Now, we'll head back to our query set. We'll add some new methods for full-timers and part-timers there, just doing the filter, again, hiding that away. And then we'll next go over to a manager and add those supporting methods there as well.
So now, when I query against this model, I have person.people.full-timers, and I'll get my full-time employees, just like you would expect. But also, I can chain. So I can say full-timers.authors. To get my full-time authors, or part-timers.authors, to get my part-time authors.
But as I was going through this, I started learning about this, I thought, there's a lot of repeated code here, right? We're just, essentially, our manager is acting as a pass-through. There's gotta be a better way to do this. So as of Django 1.7, some of you may be familiar,
there actually is a better way to do it. What we can do is we can ask Django to extract the manager straight from a query set that we provide, and it'll do it based on whatever methods that the query set has on it. So what we do to make that work is, we'll say people equals personQuerySet.asManager,
and now we can delete the manager entirely. If you're not doing anything else, just get rid of it. So now, we're only gonna have a query set with the methods that we're looking to filter by. Nobody talks about strong bad anymore, I love strong bad, it's still so good. All right, so again, healthcare.
Healthcare can be really, really tricky. There's a lot of things that I ended up doing to support our application that we had to add. Things like soft deletes, or different status fields, or things that you wanna filter away. I feel like this comes up all the time.
So managers are a terrific option at preventing mistakes that happen in your code base, and I like to joke about this as a way of preventing, or protecting future you from present you, because half the time, it's my own code that I wrote that I forget to exclude something. So the team gets back together and they say, deleting the person instances from the database
is way too heavy. We need to enable a soft delete, and you say, yeah, I got this. So you add an isActive field to your model, just a Boolean field, but it's so easy to forget to filter on that. Every time that you run a query, you have to remember to add isActive to the chain of filters, or you're gonna end up with bad data
or leaking data. So anybody here ever done something like that before? I do that all the time. And my background, I used to be in banking and now I'm in healthcare. It's like two really bad places to make mistakes like that. So anyway, so depending on your industry too, it can be harmless, inconvenience, or it can be a major compliance issue,
depending on what the situation is. So one way we can do this is we can leverage all this encapsulation that we have built into our application and really use it to our advantage. So I'm gonna add a private method here called active. I'm gonna assume that someone who's querying against my model doesn't need to use
this active method explicitly. They're just gonna get active records by using the other methods that I exposed. So all I can do, or all I need to do is internally within my full-timers, part-timers, et cetera, I'm just gonna chain in the call to active right in there. So now by default, when I query against this model,
I'm only gonna get active users. Again, no one had to do anything different. I didn't have to go rewrite all of my queries against the person model to add this active to chain it in there. I could just do it behind the scenes, which is phenomenal. You can do this with things like start dates for employee records, or there's a million different applications for it. But really anywhere that you need to hide something
that you can encapsulate that scope creep over time without causing pain. So yeah, we're gonna include only active records by default. There's only one caveat to this approach here. So you may notice we're not overriding the default query set that we're returning.
So if we were to run a .all, so person.people.all, we would get inactive records. And that's by design here. You don't have to do it this way, but there's one thing that you have to really keep in mind is that when you assume control of the default manager for a model, what you're doing is you're saying,
I wanna control the instance, control the query set that gets returned from my model under all instances. Most of the time, you're just referencing that from within your application, and that's exactly what you want. But it also counts for things like if you're running a dump data, for example. In a dump data scenario, you may want everything. So every use case is gonna be slightly different.
So your mileage may vary. It's just gonna depend on how you wanna use it. The other thing to note is that there are specific methods that you can call, or specific attributes that you can set on a method within a query set so that when you call as manager, they don't necessarily come over by default into the manager.
So for instance, no private methods come over by default when you call as manager. And there's also, there's another attribute called query set only. And if you set that, it's pretty obviously named. But query set only will prevent those methods from coming into the manager. So if you need to hide something, you can certainly do that.
Test coverage. So testing is outside the scope of this talk, but it's pretty easy to see how this is a big win for test coverage in general from within our applications. So really, what it boils down to, you only have to test the query once inside the manager. You don't need to test the query at 40 different touch points within your application.
So it's a big win there. One thing also I wanna note, there's another talk that's coming up directly after this, and it's actually in this room. It's called I Didn't Know Query Sets Could Do That. And it's actually a much deeper dive. I pared back the scope a little bit of this talk so that I didn't step on that talk.
But he's going to really dive deep into using F objects and Q objects and how you can write raw SQL queries and do really complex things with query sets. It pairs really well with this because I know what you're gonna do is you're gonna do all those complex things and then you're gonna encapsulate them inside the custom method so that you save everyone else the pain.
So hang out here. Also, I wanna give a quick shout out. This talk was really heavily inspired by the documentation for model managers that's out there. And anyone who's contributed to the docs, they're doing amazing work. When I was first getting into Python five years ago, the real reason that I was drawn towards the Django framework was just the quality
and the community support around documentation. I think, honestly, that it's unparalleled. And so give those folks a hand real quick. I just love what you're doing. Hi, thank you very much.
The bit at the end with the .all got me thinking, could you override the .all method on your query set manager, or get query sets and managers mixed up? Yeah, and put a keyword argument in it
that defaults to, say, true. So I'm thinking your is active equals true and then use that in a filter underneath. Would there be any gotchas or scary side effects of overriding .all? That's a good question.
I can't say it with an authority, but I do know that what you'd want to do is override the init of the query set in order to get what you're looking for. Obviously, it's Python object, so you can pass quarks into it. I don't see why that wouldn't work, but I was doing some experimenting with overriding init for this talk, but it just got kind of hairy in it.
I wasn't sure if it'd be beginners instance. I didn't want to get crazy, but I do believe that you could pass a quark to it if you wanted to. Only thing is is that it's a little at odds with, it would just have to be a really known quantity, I guess, for anyone who's gonna query against your model. The whole goal with this is to make it really straightforward. I was just thinking of the use case
in which 95% of the time you wanted active things and you could count on someone to specify, I want an inactive record when that came up. Yeah, yeah, you certainly could. The other thing that I've seen too is that you can actually make, so I made active a private method here, but I've seen it as a public method and then you also expose a public method
that really includes everything, but you make it some obnoxiously long name so if someone's doing it, they're totally opting in that I want everything, including bad records and deleted records and everything. So there's a lot of different ways to approach it. I think it's such a generic concept. It could go either way. Thank you. Yeah, hi, have you ever tried using model managers
to do other things besides filtering? I'm thinking just as examples like logging or initiating hooks or putting things in caches or anything like that. Yeah, absolutely, that was actually one thing that I was thinking about talking, I didn't cover, which is it's a really great way of caching because you're, because we're sort of doing this composable idea
of chaining together all these filters, you can do really, it's almost like the Russian doll caching idea where you can cache really small bits of queries and bring them all together. So absolutely, it's a really good way of doing that. We experimented with this in the healthcare thing about using this as a way of logging access to certain models.
It's a little bit hard to wedge the user context into a query but absolutely, you can do a lot of things that are not necessarily query related in there because it's just a method, so certainly. Thank you. So correct me if I'm wrong here but overriding the objects property on a model
to say like persons equals default manager, that still leaves the objects as an accessible property on that model, correct? It does not. So when you, what happens is Django will look for the first instance of a manager that it encounters and that becomes default. You could still leave, so if you're worried about backwards compatibility,
you could have two references to the same manager, one called objects, one called people and they could live next to each other but dot objects would not be available. Okay, so you're saying you have to explicitly call that out? Yes. Okay. Yeah, Django won't automatically give you it if it finds yours there first. Okay, thank you. Sure. My question kind of piggybacks off of that.
If, does that play with, if you use people instead of objects, does that play well with Django REST framework? I, you know, I couldn't answer that. I know, I would guess yes because what the ORM does is it does it, kind of what I was just saying, it does discover what instances of a manager's class
or subclass are present in your model. So it's probably leaning on the ORM to do that but I honestly, I would have to test that. It's a good question. I just really liked how your names worked out and those filter methods and stuff and yeah. Anyway, I was curious, did you have other clever ways
of naming things with when you're like doing things where you're adding additional select relateds to something? So a really common example would be listing comments on a page and you wanna do some user profile information with each comment.
You'd do a select related and it'd be a common thing you'd want with a lot of queries. Sure, so you're saying sort of query the same data two different ways, just with two different managers. Like one would include additional data, one would not. Correct, yeah. So yeah, you could certainly do that, yeah. I mean, so we, as I mentioned, you could have really an infinite number of managers living alongside of each other.
So if you were clever about it, you could probably even leverage a lot of one base query set and subclass, subclass your subclass to include the additional information in there as well. So yeah, you could definitely do that. Okay, yeah. I was more interested in, if you've personally come across that you're really good at naming things, it seems.
So I was trying to make it readable, but yeah. No, I have not personally done that. Okay, thank you. It should work, yeah. Round of applause for Sean Edmond. Thank you.