[django]select_related로 쿼리 최적화하기

dhleeone·2022년 2월 16일
0

select_related로 쿼리 최적화하기

다음은 쇼핑몰에서의 카트, 상품 옵션, 상품에 대한 모델이다.

#models.py 

class Cart(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
    inventory = models.ForeignKey(Inventory, on_delete=models.CASCADE, null=True)
    quantity = models.IntegerField(default=1, null=False, blank=False)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.user.username


class Inventory(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='inventories')
    color = models.CharField(max_length=20, null=True)
    size = models.CharField(max_length=20, null=True)
    stock = models.PositiveIntegerField(null=True)

    def __str__(self):
        return f"{self.product}, {self.color}, {self.size}, {self.stock}"


class Product(models.Model):
    name = models.CharField(max_length=200)
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, related_name='products')
    seller = models.ForeignKey(Seller, on_delete=models.CASCADE, related_name='products', null=True,default='')
    slug = models.SlugField(null=False, allow_unicode=True)
    create_at = models.DateTimeField(auto_now_add=True)
    update_at = models.DateTimeField(auto_now=True)
    description = models.TextField(blank=True)
    price = models.PositiveIntegerField(blank=True, null=True)
    sale_price = models.PositiveIntegerField(blank=True, null=True)
    display = models.BooleanField('Display', default=True)
    detail = RichTextUploadingField()   # 편집기 스타일로 입력
    image = models.ImageField(blank=True, upload_to='images/')
    
    def __str__(self):
        return self.name

카트에 담긴 상품수량과 가격으로 총 구매 예정 금액을 구하고자 할 때
cart -> inventory -> product를 거쳐 price에 접근할 수 있다.

이때 단순히 다음과 같이 코드를 작성했다고 했을 때,

# cart/views.py

# 장바구니 페이지 ------
@login_required(login_url='login')
def cart_list(request):
    current_user = request.user
    user_cart = Cart.objects.filter(user_id=current_user)

    # 총 상품 가격 표시
    total_price = 0
    for i in user_cart:
        price = i.inventory.product.price
        qty = i.quantity
    total_price += price*qty

    context = {'carts': user_cart,
                'total_price':total_price
               }
    return render(request, 'cart/list.html', context)

user_cart = Cart.objects.filter(user_id=current_user)
위와 같은 코드라면

price = i.inventory.product.price 에서 DB에 여러 쿼리를 보내면서 접근 횟수가 많아지게 된다.

하지만 다음과 같이 수정을 하면 쿼리 최적화가 가능하다.

# cart/views.py

# 장바구니 페이지 ------
@login_required(login_url='login')
def cart_list(request):
    current_user = request.user
    user_cart = Cart.objects.select_related('inventory__product')\
                .filter(user_id=current_user)

    # 총 상품 가격 표시
    total_price = 0
    for i in user_cart:
        price = i.inventory.product.price
        qty = i.quantity
    total_price += price*qty

    context = {'carts': user_cart,
                'total_price':total_price
               }
    return render(request, 'cart/list.html', context)

user_cart = Cart.objects.select_related('inventory__product')\ .filter(user_id=current_user)

select_related를 사용하면 related objects(inventory, product)까지 가져와서 cache에 저장해놓게 된다. 따라서 inventory, product를 가져오기 위해 DB에 재접근하지 않고 cache를 통해 꺼내 쓸 수 있다.

profile
하루하루 쌓아가는 개발 지식📦

0개의 댓글