Django Custom Model Manager

Django model manager

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.

By default django adds a manager “objects”, which acts as an interface through which database query operations are provided to Django models.

For this tutorial we will take employees usecase as our model example.

from django.db import models


class Employee(models.Model):

    sex = (('M', 'Male'), ('F', 'Female'))
    departments = (
       ('Sales', 'Sales'),
       ('Marketing', 'Marketing'),
       ('Finance', 'Finance'),
       ('Engineering', 'Engineering'),
    )
    roles = (
        ('J', 'Junior'),
        ('S', 'Senior'),
        ('M', 'Manager'),
    )
    first_name = models.CharField(max_length=120)
    last_name = models.CharField(max_length=120)
    email = models.CharField(max_length=200)
    gender = models.CharField(max_length=1, choices=sex)
    department = models.CharField(max_length=120, choices=departments, null=True)
    role = models.CharField(max_length=120, choices=roles, default='J')
    salary = models.FloatField()

    def __unicode__(self):
        return self.first_name + self.last_name

Modifying the model manager

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.

from django.db import models


class Employee(models.Model):

    # custom manager replaces objects manger
    all_employees = models.Manager()

    sex = (('M', 'Male'), ('F', 'Female'))
    departments = (
       ('Sales', 'Sales'),
       ('Marketing', 'Marketing'),
       ('Finance', 'Finance'),
       ('Engineering', 'Engineering'),
    )
    roles = (
        ('J', 'Junior'),
        ('S', 'Senior'),
        ('M', 'Manager'),
    )
    first_name = models.CharField(max_length=120)
    last_name = models.CharField(max_length=120)
    email = models.CharField(max_length=200)
    gender = models.CharField(max_length=1, choices=sex)
    department = models.CharField(max_length=120, choices=departments, null=True)
    role = models.CharField(max_length=120, choices=roles, default='J')
    salary = models.FloatField()

    def __unicode__(self):
        return self.first_name + self.last_name

In this example model, Employee.objects will generate an AttributeError exception, but Employee.all_employees.all() will provide a list of all Person objects.

Custom managers

You can use a custom Manager in a particular model by extending the base Manager class and instantiating your custom Manager in your model.

This helps a lot in following the “DRY” principle. We can stop writing filter queries that is repeatedly used everywhere in the project and write custom managers where it is appropriate.

Custom manager that modifies initial QuerySet

from django.db import models


class SeniorEmployeeModelManager(models.Manager):
    def get_queryset(self):
        return super(SeniorEmployeeModelManager, self).get_queryset().filter(role='S')


class ManagersModelManager(models.Manager):
    def get_queryset(self):
        return super(ManagersModelManager, self).get_queryset().filter(role='M')


class Employee(models.Model):

    # custom managers replaces objects manger
    all_employees = models.Manager()
    all_senior_employees= SeniorEmployeeModelManager()
    all_managers = ManagersModelManager()

    sex = (('M', 'Male'), ('F', 'Female'))
    departments = (
       ('Sales', 'Sales'),
       ('Marketing', 'Marketing'),
       ('Finance', 'Finance'),
       ('Engineering', 'Engineering'),
    )
    roles = (
        ('J', 'Junior'),
        ('S', 'Senior'),
        ('M', 'Manager'),
    )
    first_name = models.CharField(max_length=120)
    last_name = models.CharField(max_length=120)
    email = models.CharField(max_length=200)
    gender = models.CharField(max_length=1, choices=sex)
    department = models.CharField(max_length=120, choices=departments, null=True)
    role = models.CharField(max_length=120, choices=roles, default='J')
    salary = models.FloatField()

    def __unicode__(self):
        return self.first_name + self.last_name

In this example we have created 2 custom managers “all_senior_employees” and “all_managers”. Employee.all_senior_employees.all() returns only senior employees by modifying the queryset by adding filter by role ‘Senior’. “all_managers” modifies the queryset to return only Employees whose role is ‘Manager’. This is fairly simple example, We can use similar technique on complex queries.

Got any ideas or suggestions? Please do share with us.