My Django app ('crisismode') has two models : Events and Actions
I would like to display events and actions related to these events. For each event, I only want to display active actions, so I used a method (activactions).
How to prefetch these actions ?
class Event(models.Model):
description = models.CharField(max_length=1000, blank=True)
active = models.BooleanField(default=True)
def activeactions(self):
actions = Action.objects.filter(event=self, active=True)
if actions:
return Action.objects.filter(event=self, active=True)
else:
return None
class Action(models.Model):
description = models.CharField(max_length=1000)
active = models.BooleanField(default=True)
event = models.ForeignKey(Event, on_delete=models.SET_NULL, related_name="actionsforevent", null=True, blank=True)
I perform the following query:
events = Event.objects.filter(crisis=crisis, active=True).prefetch_related('actionsforevent')
But django-debug-toolbar shows me that there are multiple queries for it :
FROM "crisismode_action"
WHERE ("crisismode_action"."active" = true AND "crisismode_action"."event_id" = 323)
ORDER BY "crisismode_action"."order" ASC, "crisismode_action"."id"
ASC 10 similar queries. Duplicated 2 times.
Firstly I would state a part from the documentation about prefetch_related
:
prefetch_related, on the other hand, does a separate lookup for each relationship, and does the ‘joining’ in Python.
Meaning that prefetch related would make multiple queries. Moving on looking at your question you have a method activeactions
in which you try to fetch the active actions for an event. Also you try using prefetch related as .prefetch_related('actionsforevent')
. This does not work as you expect because the prefetching you do is separate from your method call. Also you don't filter in this prefetching.
For your use I believe you want to prefetch only the actions whose field active
is True
. You can do this using the Prefetch
object which can take a queryset:
from django.db.models import Prefetch
events = Event.objects.filter(
crisis=crisis,
active=True
).prefetch_related(
Prefetch(
'actionsforevent',
queryset=Action.objects.filter(active=True),
to_attr='active_actions'
)
)
We used the to_attr
argument to specify that we wanted the results on a custom attribute active_actions
.
Now we can simply access the prefetched active actions by using event.active_actions
. e.g.:
for event in events:
for action in event.active_actions:
print(action.description)