How to annotate, filter then get ForeignKey field relations in Django (FilteredRelation or another method)?

I'm trying to annotate ForeignKey relations and then use new annotated field in loop

From docs:

>>> 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")

That works. But if i do:

>>> 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)

I get AttributeError: "Restaurant object has no attribute 'pizzas_vegetarian'"

I need access to that "pizzas_vegetarian" in separate code blocks. Is it possible with FilteredRelation? Wich method should i use to get it?

The .annotate(…) [Django-doc] does not create an attribute on the Restaurant objects, this is used to do further filtering, annotating, etc. in the QuerySet.

What you need here is a Prefetch object [Django-doc] that can populate a relation in a filtered matter:

from django.db.models import Prefetch

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

If you know for sure there is at most one such vegiterian pizza, you can also retrieve it with .select_related(…) [Django-doc]:

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

I'm however not sure if that is a feature or essentially was an (unintended) side-effect of the FilteredRelation logic.

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