Hassan Baig
Hassan Baig

Reputation: 15824

Django prefetch_related query not working as required, troubleshooting needed

I have two simple Django models:

class PhotoStream(models.Model):
    cover = models.ForeignKey('links.Photo')
    creation_time = models.DateTimeField(auto_now_add=True)

class Photo(models.Model):
    owner = models.ForeignKey(User)
    which_stream = models.ManyToManyField(PhotoStream)
    image_file = models.ImageField(upload_to=upload_photo_to_location, storage=OverwriteStorage())

Currently the only data I have is 6 photos, that all belong to 1 photostream. I'm trying the following to prefetch all related photos when forming a photostream queryset:

queryset = PhotoStream.objects.order_by('-creation_time').prefetch_related('photo_set')
for obj in queryset:
    print obj.photo_set.all() 
#print connection.queries

Checking via the debug toolbar, I've found that the above does exactly the same number of queries it would have done if I remove the prefetch_related part of the statement. It's clearly not working. I've tried prefetch_related('cover') as well - that doesn't work either.

Can anyone point out what I'm doing wrong, and how to fix it? My goal is to get all related photos for every photostream in the queryset. How can I possibly do this?


Printing connection.queries after running the for loop includes, among other things:

SELECT ("links_photo_which_stream"."photostream_id") AS "_prefetch_related_val", "links_photo"."id", "links_photo"."owner_id", "links_photo"."image_file" FROM "links_photo" INNER JOIN "links_photo_which_stream" ON ("links_photo"."id" = "links_photo_which_stream"."photo_id") WHERE "links_photo_which_stream"."photostream_id" IN (1)

Note: I've simplified my models posted in the question, hence the query above doesn't include some fields that actually appear in the output, but are unrelated to this question.

Upvotes: 2

Views: 4659

Answers (1)

AKS
AKS

Reputation: 19811

Here are some of the extracts from prefetch_related:

**prefetch_related**, on the other hand, does a separate lookup for each relationship, and does the ‘joining’ in Python.

And, some more:

>>> Pizza.objects.all().prefetch_related('toppings')

This implies a self.toppings.all() for each Pizza; now each time self.toppings.all() is called, instead of having to go to the database for the items, it will find them in a prefetched QuerySet cache that was populated in a single query.

So the number of queries you see will always be the same but if you use prefetch_related then instead of hitting the database on for each photostream it will hit the prefetched QuerySet cache that it already built and get the photo_set from there.

Upvotes: 3

Related Questions