Managers in Django are perhaps a misnomer, because unlike the stereotypical PHB they are flexible and provide a very efficient way of doing things.
But while Managers are very powerful, they can cause problems if you aren’t familiar with the way they work behind the scenes. Hopefully this post will help you avoid a problem I ran into.
Some time ago our client wanted to change the way a model worked—they wanted certain objects to be invisible on the front end but still remain present on the back-end admin app.
Using a custom manager, you can accomplish that without changing the hundreds of ORM calls you have in your code. In this situation, say we want to filter out all Albums that aren’t in stock—they should never be shown in the front end. Now there are probably lots of queries like this all over your code1:Album.objects.filter(category__title="Metal")
Now, going through all such calls and adding a “in_stock=True” to every filter would be a very painful task. It can be automated with sed, but it causes a lot of unnecessary repetition.
So you can just define a custom Manager:
class AlbumInStockManager(models.Manager):
def get_query_set(self):
return super(AlbumInStockManager, self).get_query_set().filter(
in_stock=True)
And use that manager in your model:
class Album(models.Model):
objects = AlbumInStockManager()
And voila! All queries using Album.objects will now only return albums where in_stock is true.
However, this has a side effect2:
If you use custom Manager objects, take note that the first Manager Django encounters (in order by which they’re defined in the model) has a special status. Django interprets the first Manager defined in a class as the “default” Manager. Certain operations – such as Django’s admin site – use the default Manager to obtain lists of objects, so it’s generally a good idea for the first Manager to be relatively unfiltered.
Actually, make that “completely unfiltered”. The reason is explained in Ticket 1855, “Using a custom default manager can lead to un-editable objects in admin” :
This is because the admin change_stage view uses the automatic ChangeManipulator for the model, which in turn uses the model’s default manager to fetch the object to change. So if the model’s default manager happens to filter in a way which excludes that object from the returned QuerySet, the ChangeManipulator will raise ObjectDoesNotExist, which in turn causes the admin to return a 404.
Our declaration of a custom manager has resulted in overriding the default manager and breaks part of admin functionality due to this bug.
So the simple solution here is to keep “objects” as the default manager, and use a different manager for the front-end Album queries:
objects = model.Manager() # default manager
in_stock = AlbumInStockManager() # custom manager
However, this means that all previous Album.objects calls in the front-end would have to be change to Album.in_stock calls.
So, how do you have your cake and eat it too?
The correct solution for our requirements (keep Admin untouched and use the name “objects” for our custom manager):
# define all_objects first
# so it becomes the default used by admin
all_objects = models.Manager()
objects = AlbumInStockManager()
But we’re not out of the woods yet. Calls to Django helper functions like get_object_or_404 use the default manager too.
So, in the front-end where you only want in-stock Albums to be displayed, you have to change your calls:
# WRONG - uses models.manager()
one_hit_wonder = get_object_or_404(Album,
title="Who Let The Dogs Out")
# RIGHT - uses objects = AlbumInStockManager
one_hit_wonder = get_object_or_404(Album.objects,
title="Who Let The Dogs Out")
(This works as of revision 4275 which lets get_object_or_404 take Manager objects)
To recap
- The built-in admin app and various helper functions use the default manager
- The admin app will not allow editing of objects filtered out by the default manager
- This default manager will be:
- the generic one if no managers are defined
- the first defined manager if one or more managers are defined
Now I do not really like the idea of a convention, documented or no, where the first defined manager automagically becomes the default manager. Hopefully in the future there is a much clearer way of specifying the default manager regardless of where it appears in the class definition.
For more information, Ticket 1855 contains a highly informative discussion.
1 “objects” is the default Django manager that gets included with every class. It yields querysets that perform no filtering, ie return all rows
2 from model documentation

