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:

Managers | Django documentation | Django
Model Inheritance In Python Django
There are 3 styles of model inheritance possible in Python Django. Abstract base classes, Mutlitable inheritance, and proxy method.