Originally published on the Djangix blog. This is a condensed version — the full article is linked at the end.
A Django page that feels instant with a handful of rows can crawl once the table grows. Often the cause is not the database itself, but how many separate trips the ORM makes to it.
The N+1 pattern, in plain terms
Fetch a list in one query, then follow a related object for each row — and each follow-up is another query. Ten rows hide the problem; a thousand rows turn it into a thousand extra trips.
Choosing the right tool
- For a single-valued relationship — such as the one author of an article — fetch the related row together with the main row in a joined query. One trip, and later attribute access is free.
- For a collection — such as all the comments or books belonging to each item — fetch the related rows in a separate batched query and match them in Python. Trying to join collections into the main query can multiply rows and repeat the parent data.
That direction — single-valued versus collection — is the core decision rule in the original article.
Useful variations the full article covers
- Following a chain of single-valued relationships in one call.
- Restricting a prefetch to only the rows you need, for example only visible comments, and storing that filtered result under a custom attribute.
- Prefetching through a relationship into a deeper related collection.
Habits that keep you honest
- Count queries in tests and development rather than guessing — an assertion on query count catches regressions when a template starts touching a new relation.
- Watch what your templates and serializers actually access; the fix belongs where the access happens.
- Remember the limits: these helpers do not remove queries for later, separate querysets, and joining a collection can bloat results.
The short version: measure the query count, match the helper to the shape of the relationship, and re-check after you change it.
Read the full article on Djangix: select_related vs prefetch_related: The Django N+1 Fix — with worked examples and the query count at each step.
Top comments (0)