여기서 구현한 상품 정렬기능의 코드를 좀 더 효율적으로 짜보자. 이전에 짰던 코드는 다음과 같다.
class ProductView(View):
def get(self, request, *args, **kwargs):
sort_by = request.GET.get('sort_by', None)
product_info = Product.objects.select_related('harvest_year', 'measure').values(
'name',
'price',
'small_image',
'harvest_year__year',
'measure_id__measure',
'is_on_sale',
'is_in_stock',
)
if sort_by == 'title-ascending':
name_ascending = product_info.order_by('name')
return JsonResponse({'data' : list(name_ascending)}, status = 200)
elif sort_by == 'title-descending':
name_descending = product_info.order_by('-name')
return JsonResponse({'data' : list(name_descending)}, status = 200)
elif sort_by == 'price-ascending':
price_ascending = product_info.order_by('price')
return JsonResponse({'data' : list(price_ascending)}, status = 200)
elif sort_by == 'price-descending':
price_descending = product_info.order_by('-price')
return JsonResponse({'data' : list(price_descending)}, status = 200)
return JsonResponse({'data' : list(product_info)}, status = 200)
이와같이 들어온 query parameter을 로직으로 처리할경우 if문을 계속 반복해야한다.
이런경우 query parameter을 GET에 넣어줄 때 부터 정렬 매서드의 정렬기준으로 쓰이는 문자열로 보내주면 된다.
<order by에 들어갈 기준>
name : 이름순
-name : 이름 역순
price : 가격순
-price : 가격 역순
https://foodly-store.myshopify.com/collections/all-products/?sort_by=-price
위의 경우는 query parameter의 value값으로 price정렬 역순 문자열을 보냈다.
이제 받은 정렬 기준을 바로 사용해보자.
class ProductView(View):
def get(self, request):
sort_by = request.GET.get('sort_by', 'id')
offset = int(request.GET.get('offset', 0))
limit = offset + 12
product_info = Product.objects.select_related('harvest_year', 'measure').order_by(sort_by).values(
'name',
'id',
'price',
'small_image',
'harvest_year__year',
'measure_id__measure',
'is_on_sale',
'is_in_stock',
)[offset:limit]
return JsonResponse({'data' : list(product_info)}, status = 200)
위와 같이 GET으로 들어온 정렬 기준을 바로 order_by안에 넣어서 쿼리를 날려준다. 첫번째 코드처럼 if문을 하나하나 적어줄 필요 없이 가져온 value값이 기준이 되기 때문에 기준이 들어온 경우와 들어오지 않은 두가지 경우만 생각해서 코드를 작성해주면 된다.
상품상세페이지 에서는 1대1의 관계에 있는 정보들(타이틀, 제품설명, 영양정보)와 다대다관계에 있는 정보(유사상품 정보)가 같이 들어가 있다. 이 경우 1대1의 경우는 하나의 객체를 가져오고, 다대다의 경우는 여러개의 객체를 가져와야 하기 때문에 두번의 쿼리를 해야한다고 생각 할 수 있다.
prefetch_related와 select_related를 사용하면 한번의 쿼리로 위의 두가지 경우를 해결 할 수 있다.
class ProductDetailView(View):
def get(self, request, product_id):
[1] data_caching = Product.objects.filter(id=product_id).select_related('measure', 'harvest_year').prefetch_related('similar_product')
[2] product_info = data_caching.values(
'name',
'harvest_year_id__year',
'is_in_stock',
'measure_id__measure',
'description',
'price',
'small_image',
'big_image',
'energy',
'carbonydrate',
'protein',
'fat',
'mineral',
'vitamin',
'similar_product__name'
)
[3] similar_product = data_caching[0].similar_product.values(
'name',
'harvest_year_id__year',
'is_in_stock',
'measure_id__measure'
)
return JsonResponse({'data' : {'product_info' : list(product_info), 'similar_product' : list(similar_product)}}, status = 200)
[1]에서와 같이 select_related로 1대1의 관계에 있는 테이블을, prefetch_related를 사용하여 다대다 관계에 있는 테이블을 caching한다.
그리고 [2]와 [3]에 나누어 caching된 테이블에서 값을 가져온다.