Search Here

Django Create a Custom Manager with Chainable Queries

Django Managers makes handle the database access layer and they are responsible for fetching the rows from a database. In this post, you’ll learn to create a custom manager class and also chain multiple manager methods with chainable querysets.

Creating a Model

First, start by creating a model for our posts app.
In posts\models.py

from django.db import models

class PostModel(models.Model):
    post_id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255)
    content = models.TextField(null=True)
    is_active = models.IntegerField(default=1)
    
    class Meta():
        db_table = "posts"

Running Migrations

Next, do the migration for that model by running below two commands.
This command will create a migrations class for PostModel.

python manage.py makemigrations posts

This command will create a PostModel table in a database.

python manage.py migrate

Creating a Custom Manager

Inherit models.Manager class to create a custom manager.

In posts\models.py

class PostManager(models.Manager):

    def search_name( self, value ):
        return self.get_queryset().filter(name__icontains=value)

    def is_active( self, value=1 ):
        return self.get_queryset().filter(is_active=value)

We have declared a PostManager class which has two methods. search_name ( which searches the post name ) and is_active ( which set is_active condition ).

Applying Manager class to Model class

class PostModel(models.Model):
    ...
    ...

    objects = PostManager()
You can assign any name to the Manager like post_objects = PostManager() etc.

Calling the Method of Custom Manager

Open shell using command python manage.py shell

from posts.models import PostModel

records2 = PostModel.objects.is_active().search_name("my post").all()

Traceback (most recent call last):
    File "", line 1, in 
    AttributeError: 'QuerySet' object has no attribute 'search_name'

The disadvantage to custom managers is that you cannot chain or call methods of managers one after the other. And if you do you’ll see the below error:

AttributeError: 'QuerySet' object has no attribute 'search_name'

Learn more about Django Model Managers in Django Documentation

Creating a Custom Manager with Chainable Queries

We can eliminate the issue of not being able to call multiple methods in Manager by declaring models.QuerySet class.
This is how our manager will look after applying the custom class PostQuerySet.

class PostQuerySet(models.QuerySet):
    def search_name( self, value ):
        return self.filter(name__icontains=value)

    def is_active( self, value=1 ):
        return self.filter(is_active=value)
        
class PostManager(models.Manager):
    def get_queryset(self):
        return PostQuerySet(self.model, using=self._db)

    def search_name( self, value ):
        return self.get_queryset().search_name(value)

    def is_active( self, value=1 ):
        return self.get_queryset().is_active(value)

Now try to chain manager methods

Open shell again using command python manage.py shell

from posts.models import PostModel

records2 = PostModel.objects.is_active().search_name("my post").all()

Chainable Queries without Manager

If you think, using both PostQuerySet and PostManager class is overwhelming then there is a much easier way just by calling PostQuerySet.as_manager().

class PostQuerySet(models.QuerySet):
    def search_name( self, value ):
        return self.filter(name__icontains=value)

    def pk( self, value ):
        return self.filter(pk=value)

In posts\models.py

class PostModel(models.Model):
    ...
    ...

    objects = PostQuerySet.as_manager()

Using .as_manager() will make class PostQuerySet act as manager for PostModel class.

Now call the query methods

records2 = PostModel.objects.is_active().search_name("my post").all()

This will return all the posts which are active and has `my post` as a name.

Watch Video