Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django store prefetch_related result

I know I can specify to store results of prefetch_related like this.

 my_objs = MyClass.objects.prefetch_related(
     Prefetch('relation', to_attr='relation_as_list')
 )

 for my_obj in my_objs:
     l = my_obj.relation_as_list

Is it the same thing as the following? Doc says prefetch_related stores result in Queryset, and I don't get where I'm getting a performance boost?

 my_objs = MyClass.objects.prefetch_related('relation')

 for my_obj in my_objs:
     l = list(my_obj.relation.all())  # looks like DB hit but it isnt?
like image 841
eugene Avatar asked Sep 14 '25 12:09

eugene


1 Answers

These two are same

MyClass.objects.prefetch_related('relation')

MyClass.objects.prefetch_related(
   Prefetch('relation', to_attr='relation_as_list')
)

You can use the Prefetch object to further control the prefetch operation (docs)

You need a Prefetch object only when you need to refine the results of prefetch_related. For example you might not need every relation but specific ones, so you can refine the prefetch query.

Using to_attr is recommended when filtering down the prefetch result as it is less ambiguous than storing a filtered result in the related manager’s cache (docs)

to_attr doesn't give additional performance boosts, it allows making Prefetch relations less ambiguous, as well as makes it possible to prefetch same relation with different QuerySets.

MyClass.objects.prefetch_related(
   Prefetch('same_relation', queryset=queryset_one, to_attr='relation_set_one')
   Prefetch('same_relation', queryset=queryset_two, to_attr='relation_set_two')
)

If you prefetch same relation with different Prefetch objects, there will be an additional query for each of them. Resulting prefetch QuerySets will be stored in a list, but the results are not lists like you assumed as relation_as_list. They are QuerySets. In your example, accessing both relations can be done with all(), like my_obj.relation_as_list.all() and my_obj.relation.all()

About the performance boost

In summary, prefetch_related gets the related objects in one (additional) DB hit which is where the performance boost comes from. Prefetch objects lets you further refine this DB call.

for item in yourmodel.object.all():
    for a_relation in item.relation.all():
        do_something(a_relation)  
        # WITHOUT PREFETCH RELATED YOU'D HIT DB EVERY TIME!! 
        # IMAGINE IF YOU HAD TONS OF ITEMS

# THIS WILL HAVE 2 DB HITS
for item in yourmodel.object.prefetch_related('relation').all():
    for a_relation in item.relation.all():
        do_something(a_relation)  
like image 161
Tiny Instance Avatar answered Sep 16 '25 03:09

Tiny Instance