How to Add Custom Managers in Django

Blog / Django · June 23, 2019 · Updated June 10, 2026 · 8 min read
How to Add Custom Managers in Django

A custom manager in Django is a subclass of models.Manager that you attach to a model to change the default queryset behind Model.objects.all() or to add reusable, table-level query methods. You add one whenever the same filter or aggregation keeps reappearing across views, serializers and shells — instead of repeating filter(status="published") everywhere, you write it once as Article.objects.published().

Every Django model already ships with one manager named objects. This guide is the hands-on, step-by-step how-to for creating and wiring up your own. If you want the conceptual breakdown of how managers, querysets and @property differ, read the companion post on Django model managers and properties first.

Key takeaways

  • A manager is the entry point to the database; a custom manager lets you control the initial queryset or add new query methods.
  • Subclass models.Manager and override get_queryset() to filter the rows a manager returns by default (e.g. a PublishedManager).
  • For reusable filters that must chain, put them on a models.QuerySet and expose them with Manager.from_queryset() or QuerySet.as_manager().
  • The first manager declared on a model becomes _default_manager — always declare objects first so a filtered manager never silently becomes the default.
  • Set use_in_migrations = True on a manager whose methods you call inside data migrations.
  • You can attach as many named managers to a model as you like (Article.objects, Article.published, Article.featured).

What is a model manager in Django?

A manager is the interface through which database query operations are provided to a Django model. When you write Article.objects.filter(...), objects is the manager and filter() is delegated to the queryset it produces. Django adds a default manager named objects to every model automatically; you only define your own when the defaults are not enough.

There are exactly two reasons to customize a manager:

  1. Modify the initial queryset the manager returns — so Model.objects.all() is pre-filtered (covered with get_queryset() below).
  2. Add extra manager methods — table-level shortcuts and aggregates that read more clearly than raw filter()/aggregate() calls.

Manager methods can reach the model class they are attached to via self.model, which is how generic helpers stay model-agnostic.

How do you add a custom manager in Django? (step by step)

Adding a custom manager that changes the default queryset takes three steps:

  1. Define a Manager subclass that overrides get_queryset().
  2. Return a filtered queryset from get_queryset() using super().get_queryset().
  3. Attach it to the model as a class attribute — and keep the full objects manager too.

The classic example is a PublishedManager that hides drafts:

from django.db import models


class PublishedManager(models.Manager):
    """Returns only rows that are live on the site."""

    def get_queryset(self):
        return super().get_queryset().filter(status="published")


class Article(models.Model):
    title = models.CharField(max_length=200)
    status = models.CharField(
        max_length=10,
        # Django 5.x accepts a mapping for choices.
        choices={"draft": "Draft", "published": "Published"},
        default="draft",
    )

    objects = models.Manager()      # keep the full, default manager
    published = PublishedManager()  # Article.published.all() -> only published rows

    def __str__(self):
        return self.title

Now Article.objects.all() returns every row, while Article.published.all() returns only published ones. Because get_queryset() still returns a normal queryset, every chainable method works on top of it:

Article.published.all()
Article.published.filter(title__icontains="django")
Article.published.count()

You can attach as many managers as you need — featured, archived, for_author — which is the cleanest way to define common "filters" once and reuse them across the codebase.

Custom manager vs custom QuerySet: which should you use?

Methods you add directly to a manager are only available on the manager — they cannot be re-applied to an existing queryset, so they do not chain. Methods you add to a QuerySet chain freely but are not visible on the manager unless you expose them. The fix for both is Manager.from_queryset() (or its shorthand QuerySet.as_manager()), which copies your queryset methods onto the manager so they work in both places.

Approach Where the method lives Chainable after a filter? Best for
Method on models.Manager Manager class No — built from get_queryset(), can't re-apply to an existing queryset Table-level entry points and aggregates
Method on models.QuerySet QuerySet class Yes — qs.published().by_author(u) Reusable, composable filters
Manager.from_queryset(MyQuerySet) QuerySet, exposed on the manager Yes — works on the manager and stays chainable The default choice for most projects
MyQuerySet.as_manager() Same, as a one-line shorthand Yes When you need only queryset methods, no manager-only extras

Define the filters once on a QuerySet, then build the manager from it:

from django.db import models


class ArticleQuerySet(models.QuerySet):
    def published(self):
        return self.filter(status="published")

    def by_author(self, user):
        return self.filter(author=user)


class Article(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey("auth.User", on_delete=models.CASCADE)
    status = models.CharField(max_length=10, default="draft")

    # from_queryset copies every QuerySet method onto the manager, so they
    # work as Article.objects.published() AND stay chainable.
    objects = models.Manager.from_queryset(ArticleQuerySet)()

How do you keep methods chainable across a manager and a queryset?

With from_queryset() in place, the same method is callable straight off the manager and off any queryset it produces, so they compose:

Article.objects.published()                  # manager -> queryset
Article.objects.published().by_author(user)  # chains cleanly
Article.objects.filter(title__startswith="A").published()

If a model needs only the queryset methods and no manager-only helpers, skip the intermediate class entirely with as_manager():

class Article(models.Model):
    # ...fields...
    objects = ArticleQuerySet.as_manager()

Both approaches give you write-once filters. Reach for from_queryset() when you also need manager-level methods (like the aggregate below); reach for as_manager() when you do not. For composing complex OR/AND conditions inside these methods, combine them with Django Q objects.

How do you add a manager method that returns an aggregate?

Aggregates (counts, sums, averages) are table-level operations, so they belong on the manager rather than as chainable queryset filters. Extend the from_queryset() manager with a plain method that calls aggregate():

from django.db import models


class ArticleQuerySet(models.QuerySet):
    def published(self):
        return self.filter(status="published")


class ArticleManager(models.Manager.from_queryset(ArticleQuerySet)):
    """Chainable queryset filters PLUS table-level reporting helpers."""

    def total_words(self):
        result = self.get_queryset().aggregate(total=models.Sum("word_count"))
        return result["total"] or 0


class Article(models.Model):
    title = models.CharField(max_length=200)
    word_count = models.PositiveIntegerField(default=0)
    status = models.CharField(max_length=10, default="draft")

    objects = ArticleManager()

Now Article.objects.total_words() returns a single number, while Article.objects.published().count() still chains — you get reporting helpers and reusable filters from one manager. When these aggregates start driving heavy pages, profile them with the techniques in our guide to Django database access optimization, and drop down to raw SQL queries only for the rare query the ORM can't express well.

Which manager becomes the default? (the _default_manager gotcha)

The single most common custom-manager bug: the first manager declared on a model becomes Model._default_manager, which Django uses internally for related lookups, dumpdata and migrations. If a filtered manager is declared first, parts of Django silently start hiding rows.

Always declare the full objects manager first, then add named managers after it — or pin the default explicitly with Meta.default_manager_name:

from django.db import models


class PublishedManager(models.Manager):
    # Lets this manager (and its methods) be used inside data migrations.
    use_in_migrations = True

    def get_queryset(self):
        return super().get_queryset().filter(status="published")


class Article(models.Model):
    title = models.CharField(max_length=200)
    status = models.CharField(max_length=10, default="draft")

    # The FIRST manager declared becomes Model._default_manager. Declare the
    # full manager first so a filtered one never becomes the silent default.
    objects = models.Manager()      # declared first -> the default
    published = PublishedManager()  # extra, named manager

    class Meta:
        # Optional: pin the default explicitly instead of relying on order.
        default_manager_name = "objects"

How you wire managers up changes which rows the rest of your app sees. Keep the differences straight:

Pattern What you write Effect
Keep default + add named objects = models.Manager() then published = PublishedManager() Article.objects is full; Article.published is filtered
Replace the default only objects = PublishedManager() Every Article.objects call is filtered — easy to forget, hides rows in admin and related lookups
Rename the default people = models.Manager() with no objects Article.objects raises AttributeError; you must use Article.people

A related caveat: by default Django uses the base manager (an unfiltered Manager) for related-object access, so some_author.article_set.all() is not filtered by your custom manager unless you set Meta.base_manager_name. If you instead need to change save/delete behaviour or add a second Python view over the same table without touching managers at all, a proxy model is often the better tool.

Custom managers are a small change that pays off across an entire codebase — fewer repeated filters, clearer call sites and safer defaults. If you'd like a team that builds maintainable Django models like this from day one, explore our Django development services.

Frequently Asked Questions

What is the difference between a manager and a queryset in Django?

A manager is the interface attached to the model class (such as Article.objects) and is the starting point for every database query. A queryset is the lazy, chainable collection of rows that the manager produces — Article.objects.filter(...) returns a queryset. In short: the manager is where queries begin, the queryset is what flows through the chain, and get_queryset() is the bridge a manager uses to create its initial queryset.

Should I override get_queryset() or use from_queryset()?

Override get_queryset() when you want to change the default rows a manager returns, for example a PublishedManager that always hides drafts. Use Manager.from_queryset() when you want reusable filter methods that stay chainable, so Article.objects.published().by_author(user) works. Many models use both: from_queryset() for chainable filters plus a custom get_queryset() is perfectly valid.

Why did my custom manager change results everywhere?

Because the first manager declared on a model becomes its _default_manager, and Django uses that internally for related lookups, serialization and migrations. If you declared a filtered manager (like PublishedManager) first — or replaced objects with it — every default path starts hiding rows. Fix it by declaring the full objects = models.Manager() first, or by setting Meta.default_manager_name.

Can a model have more than one manager?

Yes. You can attach as many named managers to a model as you want, such as objects, published and featured. This is the recommended way to define common filters once and reuse them. The only rule to remember is ordering: the first declared manager becomes the default, so keep the unfiltered objects manager at the top.

How do I use a custom manager inside a migration?

Set use_in_migrations = True on the manager class. Django then serializes the manager into the migration state so its methods are available when a data migration (a RunPython step) accesses the historical model. Without it, the historical model in migrations falls back to a plain manager and your custom methods will not exist.

Do custom managers work with related objects (reverse foreign keys)?

Not by default. For related-object access like author.article_set.all(), Django uses the model's base manager — a plain, unfiltered manager — so your custom get_queryset() filtering is intentionally ignored to avoid hidden rows. If you genuinely need a custom base manager for related lookups, set Meta.base_manager_name to the manager you want, keeping in mind it should not filter rows out unexpectedly.

Share this article