Understanding Managers in Django ORM: Custom Managers, Default Managers, Base Managers, and Advanced Usage Patterns

This article explains how Django Managers work, how to rename them, how to create custom managers, how to override get_queryset(), how default and base managers behave, how Django uses managers for related-object access, and how to expose custom QuerySet methods through a manager. Managers are the primary interface for database operations in Django, and understanding them is essential for writing clean, reusable, and powerful ORM logic.

Django Manager, custom manager, get_queryset, default manager, base manager,related object accesscustom QuerySet, Django ORM

~6 min read • Updated Mar 10, 2026

1. What Is a Manager?

A Manager is the interface Django uses to perform database queries. Every model automatically gets at least one Manager. By default, this Manager is named objects.

Managers are responsible for:

  • Creating QuerySets
  • Providing table-level operations
  • Customizing default query behavior

2. Renaming the Default Manager

If you want to use a different name instead of objects, simply define a Manager attribute on the model:


class Person(models.Model):
    people = models.Manager()

Now Person.objects raises an error, but Person.people.all() works normally.

3. Creating Custom Managers

You can extend models.Manager to add custom methods or modify the default QuerySet.

3.1 Adding Custom Manager Methods

Custom Manager methods are ideal for table-level logic.


class PollManager(models.Manager):
    def with_counts(self):
        return self.annotate(num_responses=Coalesce(models.Count("response"), 0))

Usage:


OpinionPoll.objects.with_counts()

Manager methods can return anything—not just QuerySets.

3.2 Overriding get_queryset()

To change the default QuerySet returned by a Manager, override get_queryset():


class DahlBookManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(author="Roald Dahl")

Attach it to a model:


class Book(models.Model):
    objects = models.Manager()
    dahl_objects = DahlBookManager()

Now:

  • Book.objects.all() → all books
  • Book.dahl_objects.all() → only Roald Dahl books

4. Using Multiple Managers

You can attach multiple managers to a model to create reusable filtered views:


class AuthorManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(role="A")

class EditorManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(role="E")

Attach them:


class Person(models.Model):
    people = models.Manager()
    authors = AuthorManager()
    editors = EditorManager()

Now you can call:

  • Person.authors.all()
  • Person.editors.all()
  • Person.people.all()

5. Default Managers

The first Manager defined in a model becomes the default manager:


Model._default_manager

Django uses this manager in many internal operations (e.g., dumpdata). You can explicitly set it using:


class Meta:
    default_manager_name = "my_manager"

6. Base Managers

The base manager (_base_manager) is used when Django needs to bypass filtering—especially when retrieving related objects.

If you override the base manager using:


class Meta:
    base_manager_name = "my_base_manager"

Important: A base manager must NOT filter out rows. Django relies on it to retrieve all related objects.

7. Managers and Related Object Access

When accessing related objects (e.g., choice.question), Django uses the base manager of the related model, not the default manager.

This ensures that even filtered-out objects can still be retrieved when needed.

8. Exposing Custom QuerySet Methods Through a Manager

If you define custom QuerySet methods, you must also expose them through the Manager:


class PersonQuerySet(models.QuerySet):
    def authors(self):
        return self.filter(role="A")

class PersonManager(models.Manager):
    def get_queryset(self):
        return PersonQuerySet(self.model, using=self._db)

    def authors(self):
        return self.get_queryset().authors()

Now:


Person.people.authors()

Conclusion

Django Managers are a powerful tool for customizing how models interact with the database. You can rename them, extend them, override their QuerySets, define multiple managers, and expose custom QuerySet logic. Understanding default and base managers is essential for avoiding unexpected behavior—especially when dealing with related objects.

1. Creating a Manager with QuerySet Methods

Instead of duplicating QuerySet methods inside a Manager, Django provides QuerySet.as_manager(), which creates a Manager that automatically exposes appropriate QuerySet methods.

Example:


class Person(models.Model):
    ...
    people = PersonQuerySet.as_manager()

The resulting Manager behaves almost exactly like a manually written custom Manager.

Which methods get copied?

  • Public methods → copied by default.
  • Private methods (starting with _) → not copied.
  • Methods with queryset_only = False → always copied.
  • Methods with queryset_only = True → never copied.

Example:


class CustomQuerySet(models.QuerySet):
    def public_method(self):
        return  # available on Manager + QuerySet

    def _private_method(self):
        return  # QuerySet only

    def opted_out_public_method(self):
        return
    opted_out_public_method.queryset_only = True  # QuerySet only

    def _opted_in_private_method(self):
        return
    _opted_in_private_method.queryset_only = False  # copied to Manager

2. Using from_queryset() for Advanced Manager + QuerySet Combinations

If you want both a custom Manager and a custom QuerySet, use Manager.from_queryset(). It returns a Manager subclass that includes all appropriate QuerySet methods.

Example:


class CustomManager(models.Manager):
    def manager_only_method(self):
        return

class CustomQuerySet(models.QuerySet):
    def manager_and_queryset_method(self):
        return

class MyModel(models.Model):
    objects = CustomManager.from_queryset(CustomQuerySet)()

You can also store the generated class:


MyManager = CustomManager.from_queryset(CustomQuerySet)

class MyModel(models.Model):
    objects = MyManager()

3. Custom Managers and Model Inheritance

Rules Django follows:

  • Managers from parent classes are inherited normally.
  • If no Manager is defined, Django automatically adds objects.
  • The default Manager is:
    • the one named in Meta.default_manager_name, or
    • the first Manager defined on the model, or
    • the default Manager of the first parent model.

Example with an abstract base class:


class AbstractBase(models.Model):
    objects = CustomManager()
    class Meta:
        abstract = True

Case 1: Direct inheritance


class ChildA(AbstractBase):
    pass  # CustomManager is the default

Case 2: Override the default manager


class ChildB(AbstractBase):
    default_manager = OtherManager()

Case 3: Add extra managers without overriding the default

Solution: use another abstract base class.


class ExtraManager(models.Model):
    extra_manager = OtherManager()
    class Meta:
        abstract = True

class ChildC(AbstractBase, ExtraManager):
    pass

Now:

  • Default manager → CustomManager
  • Additional manager → extra_manager

Important note:

You cannot call Manager methods on an abstract model:


AbstractBase.objects.do_something()  # error

But calling them on concrete subclasses is valid.

4. Implementation Concerns

Django sometimes makes shallow copies of Manager instances. Therefore, your Manager must be copyable.

Example:


import copy
manager = MyManager()
my_copy = copy.copy(manager)  # must work

If you override internal methods like __getattr__, ensure you don’t break copyability.

Conclusion

Using as_manager() and from_queryset() allows you to build powerful, DRY, and flexible Managers without duplicating code. Django’s inheritance rules give you fine control over default and additional Managers. Finally, ensuring your Manager is copyable prevents subtle bugs in Django’s internal query handling.

Written & researched by Dr. Shahin Siami