In Django, how to filter a _set inside a for loop?
I have these two models:
class Convocacao(models.Model):
cursos = models.ForeignKey(Cursos)
class RegistroConvocacao(models.Model):
convocacao = models.ForeignKey(Convocacao)
I get a specific object from Convocacao:
obj = get_object_or_404(
Convocacao.objects.prefetch_related("cursos", "registroconvocacao_set"),
pk=pk,
)
Now, while the for loop runs through obj.cursos, I need to filter obj.registroconvocacao_set inside the loop:
for curso in obj.cursos.all():
obj.registroconvocacao_set.filter(...filters...)...
However, in each iteration of the for loop, obj.registroconvocacao_set.filter() makes a new query to the database, generating thousands of accesses to the database and repeated queries.
How do I prefetch obj.registroconvocacao_set to avoid this?
You've already prefetched the objects, so iterate through all of them in Python to generate a list of those which you want. For example
todo = []
for o in obj.registroconvocacao_set.all():
if( rejection_condition):
continue
if( acceptance_condition):
todo.append(o)
continue
...
for filtered_objects in todo:
...
Simple cases just have a simple test in the loop, to perform an action or not:
for o in obj.registroconvocacao_set.all():
if( condition):
... #do stuff with o
or a list comprehension
todo = [ o for o in obj.registroconvocacao_set.all() if condition ]
Yes, it would be nice if querysets recognised that what they are filtering has already been prefetched so that they could do this internally without hitting the DB again. But they don't, so you have to code it yourself·