What Is Django Model Manager?

Django Model Manager is the interface through which database query operations are provided to Django models. objects
is the default model manager and each model needs to have at least one model manager.
Let's see a few use-cases when we can use model managers and when to use custom model managers. Consider a Student
model. In case you are wondering why Student has inherited BaseModel, please read this article. Let's get back to our example.
from django.db import models
class Student(BaseModel):
name = models.CharField(max_length=30)
roll_number = models.PositiveSmallIntegerField()
gender = models.CharField(max_length=10)
is_active = models.BooleanField(default=True)
# Adding the following line will have no effect.
objects = models.Manager()
As mentioned above, all model comes with a default model manager objects
, so adding objects = models.Manager()
will have no effect. But consider a case where we want only active students, one obvious way is to have is_active=True
filter everywhere, however, a better way would be using a model manager.
from django.db import models
class ActiveStudentsManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(is_active=True)
class Student(BaseModel):
name = models.CharField(max_length=30)
roll_number = models.PositiveSmallIntegerField()
gender = models.CharField(max_length=10)
is_active = models.BooleanField(default=True)
objects = models.Manager()
active_objects = ActiveStudentsManager()
We have created our custom model manager and linked it to our Student model. In the Student model, we have inserted a total of 5 rows with 1 student as inactive
, now that we have an active_objects
model manager, let's look at how it works.
In [1]: Student.objects.count()
Out[1]: 5
In [2]: Student.active_objects.count()
Out[2]: 4
Please note that since the model manager returns a queryset
, we can use filter() exclude() and all the other QuerySet
methods on it.
We can also have custom methods on our manager. As an example, we are defining 2 custom methods to get active males and females respectively.
class ActiveStudentsManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(is_active=True)
def males(self):
return self.get_queryset().filter(gender='male')
def females(self):
return self.get_queryset().filter(gender='female')
In [2]: Student.active_objects.males()
Out[2]: <QuerySet [<Student: male: Rahul>, <Student: male: Ankit>]>
In [3]: Student.active_objects.females()
Out[3]: <QuerySet [<Student: female: Shweta>, <Student: female: Sristhi>]>
Although we have defined methods on Manger, if we chain these custom methods we will encounter AttributeError. Please note, that we will not encounter this error, if we chain standard methods which are already defined on QuerySet
class.
In [5]: Student.active_objects.males().females()
------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-5-e34a25621576> in <module>
----> 1 Student.active_objects.males().females()
AttributeError: 'QuerySet' object has no attribute 'females'
In order to fix this error, we have to extend QuerySet and define our custom method like the following.
from django.db import models
class StudentQuerySet(models.QuerySet):
def males(self):
return self.filter(gender='male')
def females(self):
return self.filter(gender='female')
class ActiveStudentsManager(models.Manager):
def get_queryset(self):
return StudentQuerySet(self.model).filter(is_active=True)
def males(self):
return self.get_queryset().males()
def females(self):
return self.get_queryset().females()
In [2]: Student.active_objects.males().females()
Out[2]: <StudentQuerySet []>
Please note that males()
and females()
methods are available only on our custom model manager which is active_objects
, what if we want these methods to be available on the default model manager i.e. objects
as well. Let's first see, what happens when we try to access these methods on the default manager.
In [3]: Student.objects.males()
-----------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-3-fa48b8327dcd> in <module>
----> 1 Student.objects.males()
AttributeError: 'Manager' object has no attribute 'males'
Let's fix this now as well and see the complete code.
class StudentQuerySet(models.QuerySet):
def males(self):
return self.filter(gender='male')
def females(self):
return self.filter(gender='female')
class ActiveStudentsManager(models.Manager):
def get_queryset(self):
return StudentQuerySet(self.model).filter(is_active=True)
def males(self):
return self.get_queryset().males()
def females(self):
return self.get_queryset().females()
class Student(BaseModel):
name = models.CharField(max_length=30)
roll_number = models.PositiveSmallIntegerField()
gender = models.CharField(max_length=10)
is_active = models.BooleanField(default=True)
objects = StudentQuerySet.as_manager()
active_objects = ActiveStudentsManager()
def __str__(self):
return '{}: {}'.format(self.gender, self.name)
We have used StudentQuerySet.as_manager()
for default manager, which will add all the methods defined on our QuerySet
to be available on the objects
manager.
In [1]: from common.models import *
In [2]: Student.objects.males().count()
Out[2]: 3
In [3]: Student.active_objects.males().count()
Out[3]: 2
In [4]: Student.objects.males().females().count()
Out[4]: 0
Resources:
