Search code examples
pythondjangodjango-modelsquery-optimizationdjango-orm

Could not figure out the use of prefetch_related in django


There is a scenario where i want to show total purchase of the user. I did using @property inside models of userprofiles. Fortunately, it is working as i expected. But i want to use prefetch_related for optimizing django queries as total purchase of user is calculated from order_history which has ManyToMany Relation with the OrderMenu.

How i did ?

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    slug = models.SlugField(unique=True,blank=True,null=True)
    restaurant = models.ManyToManyField(Restaurant, blank=True)
    order_history = models.ManyToManyField(OrderMenu, blank=True)
    # favorites = models.ManyToManyField(Restaurant)
    is_owner = models.BooleanField(default=False)

    @property
    def total_purchase(self):
        total_purchase = 0
        # user_order = UserProfile.objects.prefetch_related('order_history').get(slug=self.slug)
        userprofile = UserProfile.objects.get(slug=self.slug)
        user_order = userprofile.order_history.all()
        for usr in user_order:
            total_purchase += usr.get_cost()
        return total_purchase

In this solution, isn't

userprofile = UserProfile.objects.get(slug=self.slug) user_order = userprofile.order_history.all()

is equivalent to

user_order=UserProfile.objects.prefetch_related('order_history').get(slug=self.slug)

I am so much confused with prefetch_related. Could anyone please enlighten me with simple language? Sorry i am non-native English speaker.


Solution

  • The difference between the two is

    In case of

    userprofile = UserProfile.objects.get(slug=self.slug) #hits database

    This query gets the object of userprofile based on condition from database

    user_order = userprofile.order_history.all() #hits database again

    This query gets user_order queryset using userprofile object hitting database again.

    i.e. userprofile hits database as well as user_order

    In this case however

    user_order=UserProfile.objects.prefetch_related('order_history').get(slug=self.slug)

    user_order queryset set returns not only user profile object but also 'order_history' queryset that is related to UserProfile object in one hit to database. Now when you access 'order_history' querset with user_order object it will not hit database, but gets the queryset of 'order_history' from prefetched QuerySet cache of 'user_order'. more about it here.