A Manager is the interface through which database query operations are provided to Django models. At least one Manager exists for every model in a Django application.
The way Manager classes work is documented Making queries; this document specifically touches on model options that customize Manager behavior.
By default, Django adds a Manager with the name objects to every Django model class. However, if you want to use objects as a field name, or if you want to use a name other than objects for the Manager, you can rename it on a per-model basis. To rename the Manager for a given class, define a class attribute of type models.Manager() on that model. For example:
from django.db import models
class Person(models.Model):
#...
people = models.Manager()
Using this example model, Person.objects will generate an AttributeError exception, but Person.people.all() will provide a list of all Person objects.
You can use a custom Manager in a particular model by extending the base Manager class and instantiating your custom Manager in your model.
There are two reasons you might want to customize a Manager: to add extra Manager methods, and/or to modify the initial QuerySet the Manager returns.
Adding extra Manager methods is the preferred way to add "table-level" functionality to your models. (For "row-level" functionality -- i.e., functions that act on a single instance of a model object -- use Model methods, not custom Manager methods.)
A custom Manager method can return anything you want. It doesn't have to return a QuerySet.
For example, this custom Manager offers a method with_counts(), which returns a list of all OpinionPoll objects, each with an extra num_responses attribute that is the result of an aggregate query:
class PollManager(models.Manager):
def with_counts(self):
from django.db import connection
cursor = connection.cursor()
cursor.execute("""
SELECT p.id, p.question, p.poll_date, COUNT(*)
FROM polls_opinionpoll p, polls_response r
WHERE p.id = r.poll_id
GROUP BY 1, 2, 3
ORDER BY 3 DESC""")
result_list = []
for row in cursor.fetchall():
p = self.model(id=row[0], question=row[1], poll_date=row[2])
p.num_responses = row[3]
result_list.append(p)
return result_list
class OpinionPoll(models.Model):
question = models.CharField(max_length=200)
poll_date = models.DateField()
objects = PollManager()
class Response(models.Model):
poll = models.ForeignKey(Poll)
person_name = models.CharField(max_length=50)
response = models.TextField()
With this example, you'd use OpinionPoll.objects.with_counts() to return that list of OpinionPoll objects with num_responses attributes.
Another thing to note about this example is that Manager methods can access self.model to get the model class to which they're attached.
A Manager's base QuerySet returns all objects in the system. For example, using this model:
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
...the statement Book.objects.all() will return all books in the database.
You can override a Manager's base QuerySet by overriding the Manager.get_query_set() method. get_query_set() should return a QuerySet with the properties you require.
For example, the following model has two Managers -- one that returns all objects, and one that returns only the books by Roald Dahl:
# First, define the Manager subclass.
class DahlBookManager(models.Manager):
def get_query_set(self):
return super(DahlBookManager, self).get_query_set().filter(author='Roald Dahl')
# Then hook it into the Book model explicitly.
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
objects = models.Manager() # The default manager.
dahl_objects = DahlBookManager() # The Dahl-specific manager.
With this sample model, Book.objects.all() will return all books in the database, but Book.dahl_objects.all() will only return the ones written by Roald Dahl.
Of course, because get_query_set() returns a QuerySet object, you can use filter(), exclude() and all the other QuerySet methods on it. So these statements are all legal:
Book.dahl_objects.all()
Book.dahl_objects.filter(title='Matilda')
Book.dahl_objects.count()
This example also pointed out another interesting technique: using multiple managers on the same model. You can attach as many Manager() instances to a model as you'd like. This is an easy way to define common "filters" for your models.
For example:
class MaleManager(models.Manager):
def get_query_set(self):
return super(MaleManager, self).get_query_set().filter(sex='M')
class FemaleManager(models.Manager):
def get_query_set(self):
return super(FemaleManager, self).get_query_set().filter(sex='F')
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
sex = models.CharField(max_length=1, choices=(('M', 'Male'), ('F', 'Female')))
people = models.Manager()
men = MaleManager()
women = FemaleManager()
This example allows you to request Person.men.all(), Person.women.all(), and Person.people.all(), yielding predictable results.
If you use custom Manager objects, take note that the first Manager Django encounters (in the order in which they're defined in the model) has a special status. Django interprets this first Manager defined in a class as the "default" Manager, and several parts of Django (though not the admin application) will use that Manager exclusively for that model. As a result, it's often a good idea to be careful in your choice of default manager, in order to avoid a situation where overriding of get_query_set() results in an inability to retrieve objects you'd like to work with.
Class inheritance and model managers aren't quite a perfect match for each other. Managers are often specific to the classes they are defined on and inheriting them in subclasses isn't necessarily a good idea. Also, because the first manager declared is the default manager, it is important to allow that to be controlled. So here's how Django handles custom managers and model inheritance:
Jul 30, 2009