Custom Managers & QuerySets

A Django Manager is the interface attached to a model as Model.objects that provides database query operations and returns a QuerySet, making it the gateway through which every query begins.

Learn Custom Managers & QuerySets in our free Django course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…

Part of the free Django course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

In this lesson you'll see what the default objects manager really is, write a custom Manager that adds methods like published() and overrides get_queryset() , and build a chainable custom QuerySet with as_manager() so calls like Post.objects.published().recent() just work.

Every Django model you define automatically gets a Manager attached under the name objects . A manager is the object that provides query operations — it is the gateway to the database. When you write Post.objects.all() or Post.objects.filter(...) , you are calling methods on that manager, and what comes back is a QuerySet representing rows.

The manager itself holds very little state. Its key method is get_queryset() , which builds and returns the base QuerySet that all() , filter() , and friends start from.

To stop repeating filter(status="published") everywhere, you subclass models.Manager and add a method that wraps it. Inside the method you call self.get_queryset() and filter that, so Post.objects.published() reads like plain English.

You can also override get_queryset() itself to change the default set the manager returns. A LiveManager whose get_queryset() excludes drafts means every query through it is already filtered:

A method on a manager can only be called first — Post.objects.published() works, but Post.objects.filter(...).published() does not, because filter() returns a plain QuerySet that has never heard of published() . The fix is to put the methods on a custom QuerySet subclass, where each method returns another QuerySet and therefore chains .

PostQuerySet.as_manager() builds a manager whose methods come from the QuerySet, so the same logic is available straight off objects and after any filter() — no duplication between a manager and a queryset.

Fill in the blank so published() keeps only the rows whose status equals "published" .

❌ "QuerySet object has no attribute 'published'"

You put published() on a Manager , then called it after filter() — but filter() returns a plain QuerySet.

✅ Fix: define the method on a QuerySet subclass and use objects = PostQuerySet.as_manager() so it chains.

❌ Rows vanish from the admin after a custom manager

You overrode get_queryset() on the default objects manager, so the admin and related lookups inherited the filter.

✅ Fix: keep objects = models.Manager() unfiltered and add the filtered behaviour under a second name like live .

❌ Manager method returns a list, breaking later .filter()

Your method returned list(...) instead of a QuerySet, so chaining a queryset method afterwards fails.

✅ Fix: always return self.get_queryset().filter(...) (a QuerySet), never a materialised list.

Build a chainable QuerySet so that objects.published().recent() returns only published posts, newest first. The methods must each return a QuerySet so they can be chained.

Lesson complete — you control how your models are queried!

You now know that Model.objects is a manager and the gateway to every query, how to add methods like published() and override get_queryset() on a custom Manager , and how a chainable QuerySet subclass exposed with as_manager() lets you write Post.objects.published().recent() .

🚀 Up next: Custom Template Tags & Filters — extend the template language with your own reusable tags.

Practice quiz

What name does Django give a model's default manager?

  • objects
  • manager
  • rows
  • query

Answer: objects. Every model gets a default manager called objects.

What does a manager return when you call all() or filter()?

  • A list
  • A QuerySet
  • A dictionary
  • A Model instance

Answer: A QuerySet. Managers hand back a QuerySet representing rows.

Which manager method builds the base QuerySet it starts from?

  • build_query()
  • base()
  • get_queryset()
  • all()

Answer: get_queryset(). get_queryset() returns the base QuerySet for the manager.

Where should chainable query logic like published() live to chain after filter()?

  • In the view
  • On a custom QuerySet subclass
  • On a plain Manager
  • In settings.py

Answer: On a custom QuerySet subclass. Methods on a QuerySet subclass return QuerySets, so they chain.

What does QuerySet.as_manager() do?

  • Deletes the manager
  • Runs the query immediately
  • Builds a Manager whose methods come from the QuerySet
  • Returns a list of rows

Answer: Builds a Manager whose methods come from the QuerySet. as_manager() turns a custom QuerySet into a Manager.

Why is overriding get_queryset() on the default objects manager risky?

  • It speeds up queries too much
  • It can hide rows from the admin and related lookups
  • It deletes the database
  • It is not allowed in Django

Answer: It can hide rows from the admin and related lookups. A filtered objects manager hides rows everywhere, including the admin.

What subclass do you extend to add a custom manager?

  • models.Model
  • models.QuerySet
  • models.Manager
  • models.Field

Answer: models.Manager. A custom manager subclasses models.Manager.

Why does Post.objects.filter(...).published() fail when published() is only on the Manager?

  • filter() returns a plain QuerySet that lacks published()
  • objects is read-only
  • published() needs arguments
  • filter() deletes the manager

Answer: filter() returns a plain QuerySet that lacks published(). filter() returns a plain QuerySet, which never had published().

A custom manager method should return what to allow further chaining?

  • A materialised list
  • None
  • A string
  • A QuerySet

Answer: A QuerySet. Returning a QuerySet (not a list) keeps later .filter() working.

How do projects commonly keep objects unfiltered but still hide drafts?

  • Delete draft rows
  • Disable the admin
  • Add a second manager like live with the filter
  • Override objects.get_queryset()

Answer: Add a second manager like live with the filter. Keep objects unfiltered and add a second filtered manager such as live.