Как аннотировать, отфильтровать и затем получить отношения полей ForeignKey в Django (FilteredRelation или другой метод)?

Я пытаюсь аннотировать отношения ForeignKey и затем использовать новое аннотированное поле в цикле

Из документации:

>>> from django.db.models import FilteredRelation, Q
>>> Restaurant.objects.annotate(
...     pizzas_vegetarian=FilteredRelation(
...         "pizzas",
...         condition=Q(pizzas__vegetarian=True),
...     ),
... ).filter(pizzas_vegetarian__name__icontains="mozzarella")

Это работает. Но если я сделаю:

>>> from django.db.models import FilteredRelation, Q
>>> rests = Restaurant.objects.annotate(
...     pizzas_vegetarian=FilteredRelation(
...         "pizzas",
...         condition=Q(pizzas__vegetarian=True),
...     )
... )
... for rest in rests:
        print(rest.pizzas_vegetarian)

Получаю AttributeError: «У объекта Restaurant нет атрибута „pizzas_vegetarian“»

Мне нужен доступ к этому «pizzas_vegetarian» в отдельных блоках кода. Возможно ли это с помощью FilteredRelation? Какой метод я должен использовать, чтобы получить его?

В .annotate(…) [Django-doc] не создается атрибут на Restaurant объектах, который используется для дальнейшей фильтрации, аннотирования и т.д. в QuerySet.

Здесь вам нужен Prefetch объект [Django-doc], который может заполнить отношение в отфильтрованном виде:

from django.db.models import Prefetch

Restaurant.objects.prefetch_related(
    Prefetch(
        'pizzas',
        Pizza.objects.filter(vegitarian=True),
        to_attr='pizzas_vegetarian',
    )
)

Если вы точно знаете, что существует максимум одна такая вегетарианская пицца, вы также можете извлечь ее с помощью .select_related(…) [Django-doc]:

Restaurant.objects.annotate(
    pizzas_vegetarian=FilteredRelation(
        'pizzas',
        condition=Q(pizzas__vegetarian=True),
    ),
).select_related('pizzas_vegetarian')

Однако я не уверен, является ли это особенностью или по сути (непреднамеренным) побочным эффектом FilteredRelation логики.

Вернуться на верх