What is a "Manager" in django?
A manager is usually something hidden away from django programmers that django uses to interface between model
code and the database backend.
When you query the django ORM, you do so through calls to
from my_app.models import MyModel
mms = MyModel.objects.all()
In this case, the objects
part of the function is what is returned by the manager. If you wanted MyModel to only ever get blue
MyModel
instances (the database might contain red
models too) then you could create a manager and hack your model thus
class BlueManager(models.Manager):
def get_query_set(self):
return super(BlueManager, self).get_query_set().filter(colour='Blue')
class MyModel(models.Model):
colour = models.CharField(max_length=64)
blue_objects = BlueManager()
and calling
MyModel.blue_objects.all()
would only return objects with colour
as blue
. Note, this is a very poor way to filter models!
One would usually need to modify a Manager
interface if they were going to modify the QuerySet
s that a manager would usually return or if you needed to add "table" level queries (rather than regular django "row" level). The documentation for managers is quite complete and contains several examples.
Manager is some kind of 'gate' between application and database. One of nice thing is that you can define you own base queryset for model. For example: if you have model 'Book' with 'availability' field, you can prepare your own queryset, which filters specific kind of availability type:
models.py:
class AvailableBookManager(models.Manager):
def get_query_set(self):
return super(AvailableBookManager, self).get_query_set().filter(availability=1)
class Book(models.Model):
(...)#fields definition
objects = models.Manager() #default manager
available_objects = AvailableBookManager() #own manager
and now you can use like this:
books = Book.available_objects.all()
instead of using:
books = Book.objects.filter(available=1)
Definition
From the docs:
A Manager is a Django class that provides the interface between database query operations and a Django model.
In other words, in a Django model, the manager is the interface that interacts with the database. For example, when you want to retrieve objects from your database, you need to construct a QuerySet
via a Manager
on your model class.
By default, the manager is available through the Model.objects
property. This manager is the django.db.models.Manager
. However, it is very simple to extend it and change the default manager.
Custom managers
From the docs:
You can use a custom Manager in a particular model by extending the base Manager class and instantiating your custom Manager in your model.
There are two reasons you might want to customize a Manager (none of them are exclusive):
- to add extra Manager methods
- to modify the initial QuerySet the Manager returns
Example: adding extra methods to a Manager
from django.db import models
class DocumentManager(models.Manager):
def pdfs(self):
return self.filter(file_type='pdf')
def smaller_than(self, size):
return self.filter(size__lt=size)
class Document(models.Model):
name = models.CharField(max_length=30)
size = models.PositiveIntegerField(default=0)
file_type = models.CharField(max_length=10, blank=True)
objects = DocumentManager()
def __str__(self) -> str:
return self.name
Example: modifying the initial QuerySet the Manager returns
from django.db import models
class AuthorManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(first_name__startswith='M')
class Author(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
email = models.EmailField()
objects = AuthorManager()
def __str__(self) -> str:
return f"{self.first_name} {self.last_name}"
Example: multiple managers at the same time
It is possible to have multiple managers for the same model.
from django.db import models
from django.db.models.functions import Length
class BookTitleManager(models.Manager):
def short_titles(self):
return self.annotate(length=Length('title')).filter(length__lte=20)
def long_titles(self):
return self.annotate(length=Length('title')).filter(length__gt=20, length__lte=30)
def very_long_titles(self):
return self.annotate(length=Length('title')).filter(length__gt=30)
def starts_with(self, letter):
return self.filter(title__startswith=letter)
class BookPagesManager(models.Manager):
def small_books(self):
return self.filter(pages__lt=200)
def medium_books(self):
return self.filter(pages__gte=200, pages__lt=300)
def large_books(self):
return self.filter(pages__gte=300, pages__lte=500)
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.CharField(max_length=255)
pages = models.IntegerField()
objects = models.Manager()
titles = BookTitleManager()
sizes = BookPagesManager()
def __str__(self) -> str:
return f'{self.title} by {self.author}'
In the previous code sample, there are 3 managers: the default models.Manager
, BookTitleManager
and BookPagesManager
assigned to objects
, titles
and sizes
respectively.
The problem with the previous managers is that you cannot chain them as follows:
>>> Book.titles.long_titles().starts_with('P')
AttributeError: 'QuerySet' object has no attribute 'starts_with'
Example: Custom manager and queryset (chains are allowed)
If you want to chain methods defined in managers, you should define a custom QuerySet as follows:
from django.db import models
from django.db.models.functions import Length
class AuthorQuerySet(models.QuerySet):
def long_first_name(self):
return self.annotate(length=Length("first_name")).filter(length__gte=10)
def short_last_name(self):
return self.annotate(length=Length("last_name")).filter(length__lte=10)
class AuthorManager(models.Manager):
def get_queryset(self):
return AuthorQuerySet(self.model, using=self._db)
def long_first_name(self):
return self.get_queryset().long_first_name()
def short_last_name(self):
return self.get_queryset().short_last_name()
class Author(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
email = models.EmailField()
objects = AuthorManager()
def __str__(self) -> str:
return f"{self.first_name} {self.last_name}"
Example: Custom queryset used as manager
When defining just custom QuerySets
in the manager, it is possible to simply extend the QuerySet
and set it as the manager.
from django.db import models
from django.db.models.functions import Length
class PublisherQuerySet(models.QuerySet):
def long_name(self):
return self.annotate(length=Length("name")).filter(length__gte=15)
def long_address(self):
return self.annotate(length=Length("address")).filter(length__gte=25)
def country_starts_with(self, letter):
return self.filter(country__startswith=letter)
class Publisher(models.Model):
name = models.CharField(max_length=100)
address = models.CharField(max_length=255)
country = models.CharField(max_length=100)
objects = PublisherQuerySet.as_manager() # uses QuerySet as Manager
def __str__(self) -> str:
return self.name
Also to keep in mind
- If you want to use
objects
as a field name, or if you want to use a name other thanobjects
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.
class Document(models.Model):
name = models.CharField(max_length=30)
size = models.PositiveIntegerField(default=0)
file_type = models.CharField(max_length=10, blank=True)
stuff = models.Manager()
def __str__(self) -> str:
return self.name
In the previous code sample, calling Document.objects
will generate an AttributeError
exception because the default manager has been renamed, what will work now is Document.stuff
.
Managers are accessible only via model classes, rather than from model instances, to enforce a separation between “table-level” operations and “record-level” operations.
If a model has a ForeignKey, instances of the foreign-key model will have access to a Manager that returns all instances of the first model. By default, this Manager is named
FOO_set
, whereFOO
is the source model name, lowercased.