skip to content

In Django, what is a model Manager such as Episode.objects, and what changes when you declare a manager under another name?

level: juniorimportance: should knowfreq 52%

answer

  1. the class-level entry point for queries
  2. returns QuerySets, is not one
  3. objects is added only if none declared
  4. not reachable from an instance

basics

~20 s

A Manager is the class-level interface Django attaches to a model for database queries; its methods return QuerySets. Django adds objects only when a model declares no manager, so naming one catalog removes objects entirely.

solid answer

~40 s

In Django, every model has at least one `Manager`, the object behind `Episode.objects`. It is the entry point for table-level queries: `all()`, `filter()`, `get()`, `create()` and the other QuerySet methods are proxied through it, each call building a new `QuerySet` from the manager's `get_queryset()`. A manager is not a QuerySet itself: you cannot iterate it, and queryset-only methods such as `delete()` are deliberately not copied onto it, so you write `Episode.objects.all().delete()`. Managers live on the class; `episode.objects` raises `AttributeError: Manager isn't accessible via Episode instances`. Django creates the `objects` manager automatically only if the model (and its parents) declare none, so `catalog = models.Manager()` means `Episode.objects` no longer exists.

code

python · 17 lines
python
from django.db import models


class Episode(models.Model):
    title = models.CharField(max_length=200)
    status = models.CharField(max_length=20, default="draft")
    catalog = models.Manager()  # no automatic 'objects' now


Episode.catalog.filter(status="published")  # a QuerySet
# Episode.objects                          -> AttributeError
# Episode.catalog.delete()                 -> AttributeError (queryset_only)
Episode.catalog.all().delete()             # delete must go through a QuerySet

ep = Episode.catalog.first()
# ep.catalog                               -> AttributeError: Manager isn't
#                                             accessible via Episode instances

go deeper

for a junior

Recall that objects is a Manager on the model class that hands out QuerySets, and that it disappears if you declare a manager under another name.

for a middle

Explain get_queryset() as the source of every query, why managers are class-only, and why delete() is not on the manager.

for a senior

Show how manager naming and declaration order affect third-party code, the admin and dumpdata, and recommend keeping objects as a plain manager.

for a principal

Set a convention for where table-level logic lives in the codebase: managers, custom QuerySets or service functions, and how to keep it discoverable.

## What a manager is In Django's ORM a **`Manager`** is the interface through which database queries are made for a model. Every model has at least one. When you write: ```python Episode.objects.filter(status="published") ``` `objects` is a `django.db.models.Manager` instance attached to the `Episode` class. The manager does not hold rows or a query; each call asks its **`get_queryset()`** method for a fresh `QuerySet` and forwards the call to it. So `Episode.objects.filter(...)` is, in effect, `Episode.objects.get_queryset().filter(...)`, and `Episode.objects.all()` returns `get_queryset()` directly. Django's own `Manager` class is built with `BaseManager.from_queryset(QuerySet)`, which is why almost every `QuerySet` method is callable on it. ## Manager versus QuerySet | | `Manager` (`Episode.objects`) | `QuerySet` (`Episode.objects.all()`) | |---|---|---| | Lives on | the model **class** | nowhere; created per call | | Iterable | no (`TypeError`) | yes; evaluating runs SQL | | Holds query state | no | yes: filters, ordering, result cache | | `delete()` | not available | available | | Typical use | starting point, table-level helpers | chaining filters, evaluating | `delete()` is marked `queryset_only`, so Django refuses to copy it onto managers; `Episode.objects.delete()` raises `AttributeError`, which protects you from wiping a table with one typo. Deleting everything must be spelled `Episode.objects.all().delete()`. ## Managers belong to the class Managers are **table-level**: they manage the collection of rows, not one row. Accessing one through an instance fails: - `episode.objects` raises `AttributeError: Manager isn't accessible via Episode instances`; - on an **abstract** model, managers can be defined but not called: `AbstractBase.objects.filter()` raises `AttributeError` because there is no table; - row-level behaviour belongs in **model methods**, table-level behaviour in managers or custom QuerySets. ## Naming: `objects` is only a default Django adds a manager named `objects` **only if the model and its parents declare no manager at all**. The rules: 1. No manager declared: Django adds `objects = models.Manager()` for you. 2. Any manager declared, under any name: no automatic `objects`. 3. A model **field** named `objects` with no custom manager: Django raises `ValueError` ("must specify a custom Manager, because it has a field named 'objects'"). So this model has no `objects`: ```python class Episode(models.Model): title = models.CharField(max_length=200) catalog = models.Manager() ``` `Episode.catalog.all()` works; `Episode.objects` raises `AttributeError`. People rename the manager when `objects` is taken by a field, or to make a domain word read well, but most teams keep `objects` as the plain manager and **add** extra managers next to it, so code and third-party apps that assume `objects` keep working. ## Several managers on one model A model may declare more than one manager, each with its own `get_queryset()`: ```python class Episode(models.Model): objects = models.Manager() published = PublishedManager() # get_queryset() filters status="published" ``` - `Episode.objects.all()` returns every episode; - `Episode.published.all()` returns only published ones; - the **first** declared manager becomes the model's **default manager**, which matters to the admin, `dumpdata` and related lookups, so the order of declaration is not cosmetic. ## Custom managers: extra methods and a new starting set There are two reasons to write a `Manager` subclass, and they can be combined: - **Add table-level methods.** A method such as `create_draft(podcast, title)` or `import_feed(url)` acts on the collection, not on one row, so it belongs on the manager. It may return anything: an instance, a count, a QuerySet. - **Change the initial QuerySet** by overriding `get_queryset()`, for example `return super().get_queryset().filter(status="published")`. Every call through that manager, including `all()`, then starts from the filtered set. Row-level behaviour, such as `episode.duration_display()`, stays a model method. A manager must also be shallow-copyable, because Django copies managers internally (for example in `db_manager()`); simple subclasses are, but overriding `__getattr__` can break that. ## What to say in an interview - A manager is the class-level gateway; a QuerySet is the lazy, chainable query it hands out. - `objects` is auto-added only when nothing else is declared. - Managers are not reachable from instances and cannot `delete()` directly. - For reusable filters, the modern answer is a custom `QuerySet` exposed with `as_manager()`, because methods defined only on a manager cannot be chained after `filter()`.

  • In Django, why does Episode.objects.delete() raise AttributeError while Episode.objects.all().delete() works?
    When Django copies QuerySet methods onto a manager, it skips any method marked `queryset_only = True`, and `QuerySet.delete()` carries that mark. The design makes a whole-table delete explicit: you must first ask for a QuerySet with `all()` or `filter()`.
  • In Django, how should generic code, such as a reusable app, fetch rows of a model it does not know?
    Use `model._default_manager` (or `model._base_manager` when every row is needed) instead of assuming `model.objects` exists. A project may have renamed or replaced `objects`, and the default manager is the one Django itself uses for the model.

saying these in an interview costs you the question

  • A Manager is a QuerySet, so you can iterate over Episode.objects
  • Django always adds objects, even when other managers are declared
  • episode.objects.filter() works on an instance as well
  • Episode.objects.delete() deletes every row in the table
  • Renaming the manager also renames it in the database