ORM을 조인하고 싶을 때 N:1의 관계나 N:N의 관계일 경우, prefetch_related()를 사용하게 됩니다. 

모델 정의

예시를 들기 위해 아래와 같이 모델과 모델간 관계를 정의합니다.

from django.db import models

class Topping(models.Model):
    name = models.CharField(max_length=30)

class Pizza(models.Model):
    name = models.CharField(max_length=50)
    toppings = models.ManyToManyField(Topping)
	vegetarian = models.BooleanField()

    def __str__(self):
        return "%s (%s)" % (
            self.name,
            ", ".join(topping.name for topping in self.toppings.all()),
        )


class Restaurant(models.Model):
    pizzas = models.ManyToManyField(Pizza, related_name='restaurants')
    best_pizza = models.ForeignKey(Pizza, related_name='championed_by', on_delete=models.CASCADE)

Prefetch

만약 첫 번째 레스토랑에서 채식주의자가 먹을 수 있는 피자를 조회하는 쿼리는 아래와 같습니다.

queryset = Pizza.objects.filter(vegetarian=True)


restaurants = Restaurant.objects.prefetch_related(Prefetch('pizzas', queryset=queryset))
vegetarian_pizzas = restaurants[0].pizzas.all()


만약 위와 같은 queryset이 다른 조인 쿼리에도 사용된다면 해당 쿼리가 실행될 때마다 새로 조회를 하므로 중복조회가 발생됩니다. 이때, Prefetch()에서 제공하는 to_attr을 사용하여 쿼리를 메모리에 저장하여 효율적으로 사용할 수 있습니다.

queryset = Pizza.objects.filter(vegetarian=True)


restaurants = Restaurant.objects.prefetch_related(Prefetch('pizzas', queryset=queryset, to_attr='vegetarian_pizzas'))
vegetarian_pizzas = restaurants[0].vegetarian_pizzas


to_attr에 저장되는 Prefetch()의 데이터 크기가 너무 크지 않다면, 메모리에 올려 재사용성을 늘리는 것이 효율적입니다.

+ Random Posts