skip to content

In a Django model, what do related_name and related_query_name on a ForeignKey control, and when are you forced to set them?

level: middleimportance: must knowfreq 57%

answer

  1. the reverse side of the relation
  2. book_set versus book
  3. two keys to the same model
  4. abstract base classes and '+'

basics

~20 s

related_name names the reverse accessor on the target model (default book_set); related_query_name names the reverse lookup in filters (defaulting to related_name, else the model name). You must set them when two relations would produce clashing reverse names, including on abstract models.

solid answer

~40 s

A `ForeignKey` on `Book` pointing at `Author` also adds a reverse side to `Author`. `related_name` names the reverse **accessor**, the manager you call on an author; without it Django uses `book_set`. `related_query_name` names the reverse **lookup** used in queryset filters from `Author`; it defaults to `related_name` if set, otherwise `Meta.default_related_name`, otherwise the lowercased model name, `book`. You are forced to set them when two relations from one model point at the same target, like `Book.author` and `Book.translator`, because the default names clash and the system checks report `fields.E304`/`fields.E305`. On an abstract base class you use `%(app_label)s` and `%(class)s` placeholders so each subclass gets its own name, and a name ending in `+` turns the reverse relation off.

code

python · 28 lines
python
from django.db import models


class Author(models.Model):
    name = models.CharField(max_length=200)


class Book(models.Model):
    title = models.CharField(max_length=300)
    author = models.ForeignKey(
        Author,
        on_delete=models.PROTECT,
        related_name="authored_books",
        related_query_name="authored_book",
    )
    translator = models.ForeignKey(
        Author,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="translated_books",
    )


# author.authored_books.all()
# Author.objects.filter(authored_book__title__icontains="django")
# author.translated_books.all()
# Author.objects.filter(translated_books__isnull=False)

go deeper

for a junior

Recall that a ForeignKey adds a reverse manager on the target, named book_set unless you set related_name.

for a middle

Explain the accessor versus the query name, their defaults, and why two keys to one model or an abstract base force you to name them.

for a senior

Show naming conventions that stay readable across a large schema, and the cost of renaming a reverse name that callers already use.

for a principal

Set a project convention for reverse names, including default_related_name and the '+' pattern for audit keys, so relations read the same across apps.

## Every relation has two ends When a `Book` model declares `author = models.ForeignKey(Author, on_delete=models.PROTECT)`, Django adds two things: - A **forward accessor** on `Book`: `book.author` returns an `Author`. - A **reverse relation** on `Author`, which you never declared: a manager that returns the author's books, and a name you can use in queryset lookups that start from `Author`. `related_name` and `related_query_name` are how you name that reverse side. ## The defaults | Setting | Controls | Default | |---|---|---| | `related_name` | reverse **accessor** on the target (`author.<name>`) | `<model>_set`, e.g. `book_set` (for a `OneToOneField`, the lowercased model name) | | `related_query_name` | reverse **lookup** name in filters from the target | `related_name` if set, else `Meta.default_related_name`, else the lowercased model name, `book` | Two consequences surprise people: 1. With no `related_name`, the accessor and the lookup **differ**: you write `author.book_set.all()` but filter with `Author.objects.filter(book__title=...)`. 2. Setting `related_name="books"` renames **both**, because the query name defaults to it: now it is `author.books.all()` and `Author.objects.filter(books__title=...)`. If you want the lookup to stay singular, set `related_query_name="book"` explicitly. `Meta.default_related_name` sets a model-wide default for all of its relations, which keeps names consistent in a large app. ## When you are forced to set them Django validates reverse names with its **system checks** at startup: 1. **Two relations to the same target.** In a publisher's catalog, a `Book` has an `author` and a `translator`, both pointing at `Author`. Both would claim `author.book_set`; the checks raise `fields.E304` (reverse accessor clash) and `fields.E305` (reverse query name clash) until you give each its own `related_name`, say `authored_books` and `translated_books`. 2. **A reverse name colliding with a real field.** If `Author` already has a field called `books`, a relation cannot use that reverse name either (checks `fields.E302`/`fields.E303`). 3. **Abstract base classes.** A relation declared on an abstract model is copied into every subclass, so a fixed `related_name` would clash between subclasses. Use the placeholders: `related_name="%(app_label)s_%(class)s_related"`, where `%(class)s` is the lowercased subclass name and `%(app_label)s` its app label. 4. **Recursive many-to-many relations through an intermediary model**, where Django cannot pick distinct reverse names; at least one side needs a `related_name`. ## Turning the reverse side off Setting `related_name="+"`, or ending it with `+`, tells Django not to create the reverse accessor. It is handy for audit fields like `created_by` pointing at the user model, where you never want `user.<something>_set` and do not want to pick a name for it. ## Reading the names back A short shell session shows both names at work once `Book.author` has `related_name="authored_books"` and `related_query_name="authored_book"`: ```python author = Author.objects.get(name="Ada Park") author.authored_books.all() # reverse accessor Author.objects.filter(authored_book__title="Harbor Lights") # reverse query name Book.objects.filter(author__name="Ada Park") # forward lookup, unaffected ``` The forward side (`book.author`, `author__name` from `Book`) never changes; only the two reverse names do. If you later remove `related_query_name`, the filter name silently becomes `authored_books`, which is why a rename should be followed by a search for the old lookup strings as well as the old accessor. ## Naming guidance - Name the accessor from the **target's** point of view, plural for many: `publisher.books`, `author.authored_books`. - Keep the lookup name deliberate: plural lookups read oddly in filters, so some teams set `related_query_name` to the singular. - Prefer `settings.AUTH_USER_MODEL` for user references and give each such key a distinct `related_name`, since many models point at users. - Renaming a `related_name` touches no table, but it breaks every caller that used the old accessor or lookup, so search the codebase for both forms. Interviewers use this question to see whether a candidate knows that the reverse side exists at all, and that two separate names govern it.

  • Why does a ForeignKey with related_name="books" on an abstract base model break once two concrete subclasses exist?
    The field is copied into each subclass, so both would add `books` to the same target and the reverse accessors clash. Use `related_name="%(app_label)s_%(class)s_related"` (and the same placeholders in `related_query_name`) so every subclass gets a name derived from its own app label and lowercased class name.
  • What does related_name="+" do in Django, and when would you use it?
    It stops Django creating the reverse accessor on the target model. It suits bookkeeping keys such as `created_by = ForeignKey(settings.AUTH_USER_MODEL, ..., related_name="+")`, where nobody needs `user.<x>_set` and you would otherwise have to invent a unique name for every such field.

saying these in an interview costs you the question

  • related_name only changes the database column name of the foreign key.
  • Without related_name, filters use book_set__title as the lookup.
  • Setting related_name leaves the reverse filter name unchanged.
  • Two ForeignKeys to the same model work without any naming changes.
  • related_name="+" deletes the foreign key constraint.