class DetailProductView(View) :
def get(self, request, product_id) :
try :
product = Product.objects.prefetch_related('productimage_set').get(id=product_id)
detail_products = DetailProduct.objects.select_related('size', 'color', 'product').filter(product=product)
data_set = [{
'id' : product_id,
'name' : product.name,
'price' : product.price,
'detail' : [{
'color_id' : detail['color'],
'color_name' : Color.objects.get(id=detail['color']).color,
'size' : [{
'size_id' : size['size'],
'size_name' : Size.objects.get(id=size['size']).size
}for size in detail_products.filter(color_id=detail['color']).values('size')]
}for detail in detail_products.values('color').distinct()],
'images' : [{
'id' : image.id,
'url' : image.url
}for image in product.productimage_set.all()]
}]
return JsonResponse({'data_set' : data_set}, status=200)
except TypeError :
return JsonResponse({'message' : 'TYPE_ERROR'}, status=400)
except Product.DoesNotExist :
return JsonResponse({'message' : 'PRODUCT_DOES_NOT_EXIST'}, status=400)
detail_products
에서 filter
조건에 Path Parameter
인 product_id
를 받으려 했으나,
product
에서 역참조를 통해 상품상세페이지의 이미지를 가져와야했기 때문에
product
를 선언해야하는 상황이어서 객체로 받게 되었습니다.
prefetch_related
라서 product
객체를 가져오고 쿼리문을 실행하긴 하지만
불러온 정보를 최대한 사용하는 것이 맞다고 판단했습니다.
가장 큰 문제는 색상-사이즈에 대한 문제였습니다.
Zara 홈페이지를 보니 옷 색상마다 사이즈를 선택할 수 있어서 색상에 대한 반복문 후
그에 따라 사이즈를 선택하게 했습니다.
색상3개와 사이즈3개의 경우의 수는 총 9가지인데, detail_products
로 반복문을
돌리면 색상 3개만 필요한데 9번이 돌아가 필요없는 작업을 하게 되는 것이며
values
를 통해 접근할 수 밖에 없었습니다.
그렇다보니 색상과 사이즈의 명을 가져올 때 마다 어쩔 수 없이 쿼리문이 실행되어
select_related
를 사용했음에도 불구하고 N+1 Problem
이 발생했습니다.
select_related('color__color')
같이 사용하려 했으나, select_related
가
참조하고 있는 키 값에만 접근 가능해서 불가능하기 때문에 detail_products
의 결과에서
색상과 사이즈로 한 번 더 쪼개서 사용할 수 밖에 없었습니다.
다음 포스트는 update_or_create
혹은 F
를 이용한 수정을 해볼 생각입니다.