select_related()
coreadvancedselect_related("field") fetches a related object in the SAME query, via a SQL JOIN — but only for SINGLE-VALUED relationships: a forward ForeignKey, or a OneToOneField in either direction (forward or, via related_name, reverse). "author__hometown" chains through a FK-of-a-FK in one JOIN. It fixes N+1 for these relationship types specifically — it cannot help with a reverse ForeignKey or a ManyToManyField, since JOINing a "many" side would multiply rows (that's prefetch_related()'s job instead).
Think of it as
select_related() works because a forward FK/OneToOne relationship has exactly ONE related row per object — joining it in doesn't multiply anything, so the JOIN and the original row stay a clean 1-to-1 correspondence, safely combinable into one query with one extra set of columns. That is precisely why it CANNOT extend to a reverse FK or M2M — those are "many" on the other side, and a JOIN across a many-relationship multiplies the base row once per match, corrupting a straightforward "fetch these objects" query into something structurally different. select_related is the tool specifically for the "exactly one related row" shape; anything "many" needs prefetch_related() instead.
What we're doing: Fetch a Book along with its Author and the Author's hometown City, all in one query instead of three.
- 1
- author__hometown chains through TWO forward relationships (Book → author, author → hometown) — Django JOINs both tables into this ONE query, rather than needing a separate select_related() call for each level.
Why this works: Without chaining, select_related("author") alone would still leave book.author.hometown as its own extra query — the double-underscore syntax is what extends the same single JOIN one level further, fixing the entire chain at once instead of one level at a time.
Trying to select_related() a reverse ForeignKey or ManyToManyField
Wrong
Better
What you see: django.core.exceptions.FieldError: Invalid field name(s) given in select_related: 'entry_set'. Choices are: ... — raised immediately, since select_related() only recognizes single-valued relationship names.
Why: A reverse ForeignKey means potentially MANY Entry rows per Blog — JOINing that in directly would multiply the Blog row once per matching Entry, corrupting the query's shape entirely (a query for one Blog can't sensibly become N rows). select_related() only accepts relationships guaranteed to have at most one match, which is exactly the FK/OneToOne, forward-or-reverse-OneToOne set — anything "many" is prefetch_related()'s job, which handles the multiplication safely with a separate query instead.
- Book
- leads to author (forward FK) (select_related("author)
- author (forward FK)
- leads to hometown (forward FK) (__hometown"))
- hometown (forward FK)
- leads to one query, one JOIN
- one query, one JOIN
select_related() — what it can and can't do
Together
Remember: select_related() works ONLY for single-valued relationships (forward FK, forward/reverse OneToOne) via a SQL JOIN — never a reverse FK or M2M, which need prefetch_related() instead. field__nested chains through multiple levels in one JOIN. A long, mostly-unused chain over-fetches — pair with only() when that becomes measurable.
See also: the n plus 1 pattern · many to many and reverse fk · only and defer

