How To Create And Use Custom Model Managers In Django

We all know that model data operation in Django is so easy, you do not need to write any sql command at all, what you need to do is just use model class’s attribute objects ‘s methods to create, query, update and delete mode data records in backend database table like below. Suppose our model class name is Employee. You can read article Django Simple CRUD Operation Example to learn more about how to implement CRUD actions in Django application.

Employee.objects.create(......)

Employee.objects.all()

Employee.objects.filter(......)

emp = Employee.objects.get(......)

emp.save()

emp.delete()

1. The Default Django Model Manager.

The key of above source code is the model class’s attribute objects, this attribute is called model manager, it is an instance of django.db.models.Manager class. objects is widely used in Django code internally. It is the default model data manager for any Django model class.

Each Django model should has at least one manager, you can create multiple managers in one model class, but the first model manager object defined in the class is the default model manager. So we had better write below code before any model manager definition in model class source code so that Django will use objects as the default model manager.

objects = models.Manager()

# define other model manager in below code.

2. Create Custom Django Model Manager.

The main purpose to create custom model manager in Django is to make code reusable. This can save a lot of time in writing same query functions. You can add table level functions in model manager to implement initial QuerySet customization or provide utility methods.

2.1 Customize Initial QuerySet With Model Manager.

Your custom model manager should extends django.db.models.Manager class. If you want to customize the initial QuerySet, you can override models.Manager class’s get_queryset method like below. Below model manager will get all employees whose sex is ‘M’.

class MaleEmployeeManager(models.Manager):

    def get_queryset(self):
        return super(MaleEmployeeManager, self).get_queryset().filter(sex='M')

Then you can use above model manager in your model class source code like below. Please note the first model manager is objects = models.Manager()this can make objects as the default model manager of Employee model class. Below code create two model managers and use them in Employee model class.

class MaleEmployeeManager(models.Manager):

    def get_queryset(self):
        return super(MaleEmployeeManager, self).get_queryset().filter(sex='M')

class FemaleEmployeeManager(models.Manager):

    def get_queryset(self):
        return super(FemaleEmployeeManager, self).get_queryset().filter(sex='F')


# create Employee model, this model will be mapped to table user_register_login_employee in sqlite db.
class Employee(models.Model):

    ......

    # the first model data manager is the default manager, if defined then Employee.objects will throw error AttributeError: type object 'Employee' has no attribute 'objects'.
    # but you had better set objects = models.Manager() because Django use objects internally widely.
    objects = models.Manager()

    # create two custom model data table level manager.
    male_employee_manager = MaleEmployeeManager()

    female_employee_manager = FemaleEmployeeManager()
 
    ......

models.py full source code :

from django.db import models
from django.contrib.auth.models import User

# create Department model, this model will be mapped to table user_register_login_department in sqlite db.
class Department(models.Model):
    # define department name and description columns, the id column will be added automatically.
    dept_name = models.CharField(max_length=1000)
    dept_desc = models.CharField(max_length=1000)

    # this function will be invoked when this model object is foreign key of other model(for example Employee model.).
    def __str__(self):
        ret = self.dept_name + ',' + self.dept_desc
        return ret

    # this is a inner class which is used to define unique index columns. You can specify multiple columns in a list or tuple.
    class Meta:
        unique_together = ['dept_name']


class MaleEmployeeManager(models.Manager):

    def get_queryset(self):
        return super(MaleEmployeeManager, self).get_queryset().filter(sex='M')

class FemaleEmployeeManager(models.Manager):

    def get_queryset(self):
        return super(FemaleEmployeeManager, self).get_queryset().filter(sex='F')


# create Employee model, this model will be mapped to table user_register_login_employee in sqlite db.
class Employee(models.Model):

    # Employee has two foreign key, user(Django built-in auth_user table) and department.
    user = models.ForeignKey(User, on_delete=models.CASCADE,)

    # support department and employee table are many to many relationship.
    dept = models.ManyToManyField(Department)

    emp_mobile = models.CharField(max_length=100)
    # should add the () after IntegerField, otherwise the emp_salary column will not be created.
    emp_salary = models.IntegerField()
    # if do not specify the auto_now=True attribute with value then this field can not be created.
    emp_onboard_date = models.DateTimeField(auto_now=True)


    # add a sex field which value is limited to a choice tuple.
    sex_choices = (
        ('M', 'Male'),
        ('F', 'Female'),
    )
    sex = models.CharField(max_length=2, choices=sex_choices, default='M')

    # the first model data manager is the default manager, if defined then Employee.objects will throw error AttributeError: type object 'Employee' has no attribute 'objects'.
    # but you had better set objects = models.Manager() because Django use objects internally widely.
    objects = models.Manager()

    # create two custom model data table level manager.
    male_employee_manager = MaleEmployeeManager()

    female_employee_manager = FemaleEmployeeManager()

    # this method will be called when other model reference Employee model as foreign key.
    def __str__(self):
        return self.user.username + ',' + str(self.emp_salary) + ',' + str(self.emp_mobile)

    class Meta:
        unique_together = ['emp_mobile']

2.2 Add Table Level Utility Functions In Model Manager.

Now we add another model manager as below, it provide one utility method which can get total employee count number.

class EmployeeManager(models.Manager):

    def get_total_employee_count(self):
        return self.all().count()

Now we define above model manager at the bottom of Employee model class. Then we can use the model manager in python source code or shell command.

class Employee(models.Model): 
   ......
   # the first defined model manager is the default model manager.
   objects = models.Manager()

   # create two custom model data table level manager.
   male_employee_manager = MaleEmployeeManager()

   female_employee_manager = FemaleEmployeeManager()

   employee_manager = EmployeeManager()

   ......

2.3 Customize QuerySet In One Model Manager.

We can define two model managers to initialize different QuerySet like above in section 2.1, but we can also customize QuerySet in one same model manager to implement same functions also.

READ :   How To Force Reset Django Models Migrations

To customize QuerySet, you need to make your custom QuerySet class extends django.db.models.QuerySet class, then define different methods to retrieve different QuerySet.

class EmployeeQuerySet(models.QuerySet):

    def male_employee_queryset(self):
        return self.filter(sex='M')

    def female_employee_queryset(self):
        return self.filter(sex='F')

Then create and return above customize QuerySet object when you override the get_queryset method in the custom model manager like below. Below customize model manager also add two methods, male_employee and female_employee. It invoke different EmployeeQuerySet methods to get different filtered QuerySet.

class EmployeeManager(models.Manager):

    def get_queryset(self):
        return EmployeeQuerySet(self.model, using=self._db)

    def male_employee(self):
        return self.get_queryset().male_employee_queryset()

    def female_employee(self):
        return self.get_queryset().female_employee_queryset()

    def get_total_employee_count(self):
        return self.all().count()

3. How To Run Above Custom Model Manager In Django Shell.

Now you can run above model manager class in Django shell, but when you change the custom manger source code, you need to exit the shell and enter again to make effect.

  1. Open terminal and go to Django project root folder.
  2. Run $ python3 manage.py shellcommand in terminal.
  3. Then you enter the shell and can invoke this Django project model class and custom mangers as below.
    In [1]: from dept_emp.models import Employee, Department
    
    In [2]: Employee.employee_manager.get_total_employee_count()
    Out[2]: 1
    
    In [2]: Employee.employee_manager.male_employee()
    Out[2]: <EmployeeQuerySet []>
    
    In [3]: Employee.employee_manager.female_employee()
    Out[3]: <EmployeeQuerySet [<Employee: jerry,80000,13919283928>]>
    
    In [4]: exit()
    

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.