Для чего используется атрибут related_query_name в моделях Django

Ответ

Атрибут related_query_name в полях ForeignKey, OneToOneField и ManyToManyField в Django используется для кастомизации имени, по которому можно фильтровать объекты из связанной модели.

Он решает проблему неоднозначности и делает обратные запросы (reverse lookups) более читаемыми, особенно когда на одну модель есть несколько ссылок.

Ключевое отличие от related_name:

  • related_name: Имя для доступа к связанным объектам от экземпляра. Например, author.books.all().
  • related_query_name: Имя для использования в фильтрах (filter, exclude) на уровне QuerySet. Например, Author.objects.filter(book__title=...).

Если related_query_name не задан, Django по умолчанию использует значение related_name.

Пример:

Предположим, у пользователя есть два типа постов: созданные и отредактированные.

from django.db import models
from django.contrib.auth.models import User

class Post(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(
        User, 
        on_delete=models.CASCADE, 
        related_name='authored_posts',
        related_query_name='authored_post' # Имя для фильтрации
    )
    last_editor = models.ForeignKey(
        User, 
        on_delete=models.CASCADE, 
        related_name='edited_posts',
        related_query_name='edited_post' # Имя для фильтрации
    )

Теперь мы можем выполнять интуитивно понятные запросы:

# Найти всех пользователей, которые являются авторами постов с заголовком 'Django Intro'
User.objects.filter(authored_post__title='Django Intro')

# Найти всех пользователей, которые редактировали посты с заголовком 'Django Intro'
User.objects.filter(edited_post__title='Django Intro')

Без related_query_name пришлось бы использовать authored_posts__title и edited_posts__title, что может быть менее очевидно.

Ответ 18+ 🔞

А, слушай, вот эта штука related_query_name в Django — она как та самая хитрая жопа, которая всегда прячется за углом, пока ты не наступишь на неё. Вроде бы всё понятно, а потом бац — и ты уже в дебаге сидишь, блядь, три часа, потому что запрос не работает.

Короче, представь себе такую хуйню. У тебя есть модель Post и модель User. И вот ты такой: "О, я умный, я сделаю две связи на одну модель!" И пишешь:

class Post(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='authored_posts')
    last_editor = models.ForeignKey(User, on_delete=models.CASCADE, related_name='edited_posts')

Всё вроде окей. related_name — это как кличка, по которой из юзера можно до его постов достучаться. user.authored_posts.all() — вот это вот всё.

Но потом ты хочешь отфильтровать юзеров по постам. И пишешь, по привычке:

# Хочу всех юзеров, которые написали пост с заголовком 'Про Django'
User.objects.filter(authored_posts__title='Про Django')

А потом:

# Хочу всех юзеров, которые редактировали пост с заголовком 'Про Django'
User.objects.filter(edited_posts__title='Про Django')

И тут вроде всё логично, ёпта. authored_posts и edited_posts — это же related_name. Но представь, что related_name у тебя какой-нибудь заковыристый, вроде posts_where_this_dude_was_the_author. Ну, для читаемости в коде. И тогда твой фильтр превращается в:

User.objects.filter(posts_where_this_dude_was_the_author__title='Про Django')

Выглядит как бред сумасшедшего, блядь. И вот тут на сцену выходит наш герой — related_query_name. Это как псевдоним специально для фильтров, чтобы не позориться.

class Post(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name='posts_where_this_dude_was_the_author', # Пусть будет так, для прикола
        related_query_name='author_of' # А вот это — для фильтров!
    )
    last_editor = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name='posts_where_this_dude_messed_up',
        related_query_name='editor_of'
    )

И теперь, в рот меня чих-пых, магия:

# Ищем юзеров, которые написали пост про Django
User.objects.filter(author_of__title='Про Django')

# Ищем юзеров, которые его редактировали
User.objects.filter(editor_of__title='Про Django')

Красота же, блядь? Чисто, понятно, без этих многоэтажных posts_where_this_dude_was_the_author.

А если не задать related_query_name? Да похуй, Django возьмёт значение из related_name и будет использовать его. Так что если тебя устраивает твой posts_where_this_dude_messed_up в фильтрах — можешь не париться.

Короче, суть в чём: related_name — это как обращаться от юзера к его постам. А related_query_name — это как обращаться к юзеру из запроса, когда фильтруешь по связанным постам. Чтобы не было путаницы, когда связей на одну модель — овердохуища.

Запомни, и всё у тебя будет пиздато. А не запомнишь — будешь как Герасим, только "Му-му" мычать, глядя на ошибку FieldError.