How to create a custom manager django?

Table of contents

How to create a custom manager django?

A Manager (or handler) is the interface through which query operations or queries from the database are provided to Django models. Yes, I mean that objects that goes after the name of your model; YourModel.objects.all() and Tumodel.objects.filter(). All Django models have at least one manager. Whenever you use the object manager (I will refer to it as manager from here on) in a database query using the Django ORM you are making use of its default object manager. These managers in Django can be customized to modify the objects returned by a query and we can customize them to our liking.

Before you start, if you don’t know the basics of Django you can start with the definitive guide to Django

On the other hand, if you are looking to optimize your Django app, probably my post where I talk about how to improve performance of slow Django apps will serve you better.

The Django object manager

If you have used the Django ORM, you have probably already used the default manager. Objects is the name of the default manager and is responsible for returning all objects in a Django model.

Videogame.objects.all()

Modifying the default manager

Perhaps we want to have two managers, one that returns all objects and another that returns the most recent objects, or objects created by a particular user, or objects filtered by a term.

Let’s start by modifying the name of the default manager, to do so we just have to assign it to the Manager object of models.

from django.db import models

  class Videogame(models.Model):
  ...#
      stem = models.Manager() #Allows you to call Videogame.stem.all() instead of Videogame.objects.all()

What is this for? Well, we can now call the object manager in a different way, which may improve the readability of our code, but it is not the most important reason.

Videogame.stem.all()

Adding methods to a Django manager

A custom manager allows us to add new methods, which will give it unique behaviors. How? You can filter the results of a search, limit the results according to the user, a range of dates, a number of results, whatever you prefer.

Look at this example below, we instantiate a new manager called VideogameManager, which inherits from models.Manager. We add a method called count_titles that will be in charge of counting the results for a given search, nothing too complicated, we simply concatenate a filter with a query, as if it were any search.

Since we have this new manager with the count_titles method, we replace the objects property of our Videogame model with an instance of the manager we just created.

from django.db import models

  class VideogameManager(models.Manager):
      def count_title(self, keyword):
          return self.filter(title__icontains=keyword).count()
   #self refers to manager itself
  class Videogame(models.Model):
    
      objects = VideogameManager() # rename default manager, objects will return something else instead of all

Now our default manager, objects, has a method called count_titles that we can use as if it were part of the original Django ORM.

Videogame.objects.count_title('fantasy')

Modifying the initial Manager QuerySets

A Manager’s base QuerySet returns all objects in the system. But what if we are only interested in certain data? Imagine that the online store has a database of all video games, but, since we are basic geeks, we gave the squarenix company a special section.

If we write individual custom queries for each queryset in that section it would look something like this:

Videogames.objects.filter(company="squarenix").filter(title__icontains="Fantasy")
# ...
Videogames.objects.filter(company="squarenix").filter(description__icontains="Aventura")
# ...
Videogames.objects.filter(company="squarenix").filter(genre="RPG")

As you know, the above repeats too much code, violating the DRY maxim.

We can replace the base QuerySet by overwriting the Manager.get_query_set() method so that the default queryset we get does the filtering by company name.

from django.db import models

  # define a subclass for manager
  class SquarenixManager(models.Manager):
      def get_query_set(self):
          return super(SquarenixManager, self).get_query_set().filter(company='squarenix')

  # we set it to videogame model explicitly
  class Videogame(models.Model):
      # ...
      objects = models.Manager() # Set default manager
      squarenix_videogames = SquarenixManager() # Our custom manager

Note how we now have two managers. A Model can define several managers, the first manager that appears is the default manager (in the example above it is objects), which will be used by Django internally for other special features.

When running the manager it will return only the books that have Squarenix as company and, in addition, you can use all QuerySet methods on it.

Videogame.objects.all() # Return all videogames, no filtering
Videogame.squarenix_videogames.all() # Only return squarenix videogames
Videogame.squarenix_videogames.filter(title__icontains='Kingdom Hearts') #Return only squareenix videogames whose name contains Kingdom Hearts

And that’s all. Now that you know that you can create as many managers as you want that will give you as many filtered searches as you need.

If you want to know more about managers, please check the official Django documentation

Eduardo Zepeda
Eduardo Zepeda
Just call me Ed. Web developer and GNU/Linux preacher. Maturity over novelty, better done than perfect. I used to believe in the goodness of cryptocurrencies outside of monetary speculation.