Django Prefetch Related Still Queries

I have the function

def selected_location_equipment(self):
    qs = (Equipment.objects
            .prefetch_related('service_logs')
            .all())

    return qs

That returns a queryset with a few related fields grabbed.

The problem is that when i access the prefetched data later in my code, it executes a query again.

Ive stepped through the Django code and can see where it is checking the cache for the .all() in one spot and doesnt query, but then when its called here, it's almost like the cache is cleared.

Debug Toolbar shows a query for each iteration of the loop as well.

for e in equipments:
   last_service = list(e.service_logs.all())[-1]
   for log in e.service_logs.all():
        # do other stuff
        ...

Here's the basic model definition for Equipment

class ServiceLog(models.Model):
    equipment = models.ForeignKey(Equipment, 
                                  on_delete=models.CASCADE, 
                                  related_name='service_logs')

You want to fetch only the last service log per equipment and avoid unnecessary memory consumption, you can also use annotate() or Prefetch with filtering:

from django.db.models import Prefetch

# Prefetch only the latest service log
qs = Equipment.objects.prefetch_related(
    Prefetch('service_logs', queryset=ServiceLog.objects.order_by('-id'), to_attr='prefetched_service_logs')
)

for e in qs:
    last_service = e.prefetched_service_logs[0] if e.prefetched_service_logs else None

Probably the issue is that you are using .all() in the loop making query to run again. If you want to avoid extra queries you fetch the data only once:

for e in equipments:
   service_logs = list(e.service_logs.all()) 

   last_service = service_logs[-1]  
  
   for log in service_logs:
      ...

Although, it is strange that debug Toolbar shows a query for each iteration because Django should cache the result according to the docs. If you do actions to a log during iterate the queries might be connected to that, though.

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