How to Create a Custom Manager With Chainable QuerySets in Django

Django manager interacts with the database and makes it possible to create very powerful custom querysets

Picture of Nsikak Imoh, author of Macsika Blog
White background with the text How to Create a Custom Manager With Chainable QuerySets in Django
White background with the text How to Create a Custom Manager With Chainable QuerySets in Django

This post is part of the tutorial series titled Learn to Use Django with FastAPI Frameworks

Table of Content

Most developers who work with Django tend to ignore the capability of the model manager in developing powerful custom querysets for a model. It might also surprise you that some do not know about the Django manager and how it works within a model.

The Django model manager is the interface that Django uses to interact with the database.

The default manager every Django model gets out of the box is the django.db.models.Manager and it is made available through the Model.objects property. It is very straightforward to extend it and change the default manager.

The ability to extend and change the default manager makes it possible to create very powerful custom querysets for a django model. It also allows for the possibility of creating a generic behavior for objects throughout the entire application.

In this post, you will learn how to create a custom model manager with a chainable queryset in Django.

Sample model and model manager

Let's work with a model that holds some information about students for our code demo:

Model for Student

from django.db import models

class Student(models.Model):
    name = models.CharField(max_length=30)
    score = models.PositiveIntegerField(default=0)
    hobby = models.CharField(max_length=10, blank=True)

    objects = StudentManager()
Highlighted code sample.

Model Manager for Student Model

Here's what a typical model manager will look like:

from django.db import models

class StudentManager(models.Manager):
    def dance(self):
        return self.filter(hobby='dance')

    def grade(self, score=95):
        return self.filter(score__gt=score)
Highlighted code sample.

With the model manager defined above, you will be able to retrieve all students whose hobbies are dance using the line below:

Student.objects.dance()
Highlighted code sample.

But, here's something else you should know. The methods we defined above are not chainable. This means that despite being able to still use order_by or filter in the result, like below

Student.objects.pdfs().order_by('name')
Highlighted code sample.

If you try to chain the methods on themselves, it will break and cause an Attribute error.

Student.objects.pdfs().smaller_than(1000)
Highlighted code sample.
AttributeError: 'QuerySet' object has no attribute 'smaller_than'
Highlighted code sample.

To make the methods chainable, you must create a custom QuerySet method:

class StudentQuerySet(models.QuerySet):
    def dance(self):
        return self.filter(hobby='dance')

    def grade(self, score=95):
        return self.filter(score__gt=score)
Highlighted code sample.

Here's what the model manager will now look like:

from django.db import models

class StudentManager(models.Manager):
	def get_queryset(self):
        return StudentQuerySet(self.model, using=self._db)  # Take note of this new method
        
    def dance(self):
        return self.self.get_queryset().dance()

    def grade(self, score=95):
        return self.get_queryset().grade(score)
Highlighted code sample.

Now you can use it just like any other QuerySet method:

Student.objects.dance().grade(98).exclude(name='Adam').order_by('name')
Highlighted code sample.

If you are only defining custom QuerySets in the Manager, you can simply extend the models.QuerySet and in the model set the manager as objects = StudentQuerySet.as_manager():

class Student(models.Model):
    ...

    objects = StudentQuerySet.as_manager()
Highlighted code sample.

Organizing the Model and Model Manager Codes

When you're simply testing something or working on a small project, you can keep the code inside the models.py. But, when working on a project with a propensity for the code base to grow, I recommend you keep the Managers and QuerySets in a different module, named managers.py.

Wrap Off

The Django model manager acts as the interface that Django uses to interact with the database.

The ability to extend and change the default manager makes it possible to create very powerful custom querysets for a django model. It also allows for the possibility of creating a generic behavior for objects throughout the entire application.

If you learned from this tutorial, or it helped you in any way, please consider sharing and subscribing to our newsletter.

Please share this post and for more insightful posts on business, technology, engineering, history, and marketing, subscribe to our newsletter.

Get the Complete Code of Django and FastAPI Combo Tutorials on Github.

Connect with me.

Need an engineer on your team to grease an idea, build a great product, grow a business or just sip tea and share a laugh?