skip to content

In Django, which queries does Meta.ordering apply to, and what does a default ordering cost?

level: middleimportance: should knowfreq 44%

answer

  1. ORDER BY you did not write
  2. every query without order_by()
  3. GROUP BY is the exception
  4. order_by() with no arguments

basics

~20 s

Meta.ordering adds an ORDER BY to every query on the model that does not call order_by(), including related managers and first()/last(). Aggregation queries with GROUP BY skip it. The cost is a sort on every such query.

solid answer

~40 s

`Meta.ordering` is the default `ORDER BY` Django adds to any queryset of the model that has no `order_by()` of its own: list views, related managers such as `user.subscription_set.all()`, `first()` and `last()`, and the admin changelist unless it sets its own ordering. It is not applied to `GROUP BY` queries such as `values().annotate()`. The cost is that every one of those queries sorts, which the database does cheaply only when an index matches the ordering; ordering by a foreign key also pulls in the related model's own default ordering and a join. `order_by()` with no arguments removes it for one query. I set it only when an order is intrinsic to the model and include a unique tie-breaker, and otherwise order explicitly where the list is built.

code

python · 9 lines
python
from django.db.models import Count

# Meta.ordering = ["-started_at", "-id"] on Subscription

Subscription.objects.filter(status="active")          # ORDER BY started_at DESC, id DESC
user.subscription_set.all()                            # same default ordering
Subscription.objects.order_by("renews_at")             # replaces the default
Subscription.objects.filter(status="active").order_by()  # no ORDER BY at all
Subscription.objects.values("plan").annotate(n=Count("id"))  # GROUP BY: default not applied

go deeper

for a junior

Recall that Meta.ordering sets a default sort and that order_by() overrides it.

for a middle

Explain which queries receive the default ordering, the GROUP BY exception, and how order_by() with no arguments clears it.

for a senior

Diagnose a slow list caused by a hidden default sort, match indexes to the ordering, and decide where explicit ordering beats a model-wide default.

for a principal

Set a team rule for default ordering: when it is intrinsic to a model, when it must be explicit, and how tie-breakers keep pagination stable.

## What Meta.ordering does `Meta.ordering` is a list of field names or expressions, such as `["-started_at"]` (descending) or `[F("renews_at").asc(nulls_last=True)]`. It becomes the **default ordering** of the model: Django adds the matching `ORDER BY` to a query whenever that query has not been given an ordering explicitly. Without any ordering, a SQL database returns rows in an **unspecified order**, which can change between runs. That is the problem `Meta.ordering` solves, and why teams add it. ## Where it applies - `Subscription.objects.all()` and any filtered queryset without `order_by()`. - Related managers: `user.subscription_set.all()` is a queryset of `Subscription`, so it is ordered too. - `first()` and `last()` use the default ordering; on an unordered queryset they order by the primary key instead. - The admin changelist uses it unless the `ModelAdmin` sets its own ordering. - Passing an unordered queryset to `Paginator` emits `UnorderedObjectListWarning`; a default ordering silences that, which is one reason people add it. Where it does **not** apply: 1. **`GROUP BY` queries.** Since Django 3.1 the default ordering is not used in aggregation queries such as `Subscription.objects.values("plan").annotate(n=Count("id"))`. Before that, it leaked into the `GROUP BY` and split groups; code written for old versions sometimes still calls `order_by()` there defensively. 2. **Queries that override it.** Any `order_by(...)` call replaces it, and `order_by()` with **no arguments** clears ordering entirely, including the default. ## What it costs | Cost | Why | |---|---| | A sort on every list query | each query now carries `ORDER BY`, even where the caller does not care | | A sort the database cannot skip | without an index matching the ordering, the database must sort all matching rows before returning the first page | | Hidden joins | ordering by a foreign key (`ordering = ["plan"]`) means ordering by the plan's own default ordering, which may need a join | | Random order | `"?"` is expensive on large tables | | Surprise for readers | a queryset's order comes from a class they may not have opened | Django's own documentation warns that ordering is not free, and that each foreign key in the ordering brings in that model's default ordering as well. ## Designing a good default - Order by something **unique** or add a tie-breaker. `["-started_at"]` alone leaves rows with the same timestamp in an unstable order; `["-started_at", "-id"]` does not. - Make sure the database can serve it: an index on the ordering columns, or on the filter plus ordering columns used by the busiest list. - Prefer **explicit ordering at the call site** for lists with a specific purpose (renewals due soonest first, invoices by amount), and keep `Meta.ordering` for the order that is intrinsic to the model. - For internal queries that do not need order, such as bulk exports or data clean-up scripts, call `order_by()` to drop the sort. ## Checking what a queryset will do Two small tools make the hidden default visible: 1. `queryset.ordered` is `True` when the queryset has an explicit `order_by()` or a default ordering that applies (and `False` for `GROUP BY` queries relying only on the default). 2. `print(queryset.query)` shows the SQL Django will send, including any `ORDER BY` inherited from `Meta`. Checking both in a shell before blaming the database often reveals that a view never asked for the sort it is paying for. ## Diagnosing a slow list A typical production symptom: a table grows to millions of rows and a simple page like "latest subscriptions" becomes slow. The query plan shows a full sort of matching rows. The fixes, in order of preference, are adding an index that matches the filter and ordering, ordering explicitly by an indexed column in that view, or removing a default ordering nobody relies on. Because the default is applied silently, the first step is noticing that the `ORDER BY` came from `Meta` rather than the view.

  • What does Subscription.objects.first() order by in Django when the model has no Meta.ordering?
    It orders by the primary key. `first()` uses the queryset's ordering when there is one, from `order_by()` or `Meta.ordering`; otherwise it adds `order_by("pk")` so the result is deterministic. `last()` does the same in reverse.
  • Why can Meta.ordering = ["plan"] add a join in Django?
    Ordering by a foreign key means ordering by the related model's own default ordering. If `Plan` has `ordering = ["name"]`, Django must join `Plan` to sort subscriptions by plan name. Use `plan_id` to sort by the key column itself without the join.

saying these in an interview costs you the question

  • Meta.ordering only affects the admin and never the SQL of normal queries.
  • Without Meta.ordering, rows always come back in insertion order.
  • Meta.ordering is also applied inside values().annotate() GROUP BY queries.
  • A default ordering costs nothing because rows are stored sorted.
  • order_by() with no arguments keeps the Meta.ordering default.