상품리스트 조화 API의 분기를 줄이는 과정에 대한 포스팅이다.
기존의 상품 리스트조회 API코드를 살펴보면
class ProductsView(View):
@input_validator
def get(self, request):
option = request.GET.get('option', None)
offset = int(request.GET.get('offset', 0))
limit = int(request.GET.get('limit', 10))
order = request.GET.get('order', 'id')
search = request.GET.get('search', None)
category = request.GET.get('category', 0)
q = Q()
if option == 'new' or category == 'new':
q = Q(is_new=True)
category_name = 'NEW'
if option == 'sale' or category == 'sale':
q = Q(~Q(discount_percent=0))
category_name = 'SALE'
if (category != 'new' and category != 'sale') and category:
q &= Q(category_id=category)
category_name = categories[category]
if search:
q &= Q(name__icontains = search)
category_name = search
products = Product.objects.filter(q).prefetch_related('image_set')\
.annotate(avg_rate=Avg('comment__rate'),popular=Count("userproductlike", distinct=True),review_count=Count('comment',distinct=True))\
.order_by(order)[offset:offset+limit]
이하 생략
쿼리 파라미터에 들어온 값을 합치는 부분에서
q = Q()
if option == 'new' or category == 'new':
q = Q(is_new=True)
category_name = 'NEW'
if option == 'sale' or category == 'sale':
q = Q(~Q(discount_percent=0))
category_name = 'SALE'
if category:
q &= Q(category_id=category)
category_name = categories[category]
if search:
q &= Q(name__icontains = search)
category_name = search
총4번의 분기가 생긴다.
분기가 많다 -> 테스트코드의 양이 많아진다. , 디버깅이 어려워진다.
이런 문제점을 인식하고 4개의 분기를 1개로 줄여보겠다.
4개의 조건문들을 살펴보면 쿼리파라미터에 값이 있거나 원하는 값이여야 한다는 조건이다.
그래서
request.GET.items()
를 사용할 것이다.
이렇게 하면 쿼리파라미터 키값과 값을 쌍으로 갖고 있는 이터레이블 객체가 나온다.
이터레이블 객체를 풀어주기 위해 아래와 같이 한다.
{i : v for i,v in request.GET.items()}
예를 들어서 쿼리 파라미터 order에 id category에 3을 넣고 위 코드를 실행하면
{'order' : id, category : 3}
과 같은 형태의 딕셔너리가 만들어진다.
그렇다 이렇게 하고 쿼리 파라미터를 안주면 딕셔너리에 안 담기게 되고 쿼리 파라미터에 값이 있는지 없는지를 검사하지 않아도 된다.
그리고 이렇게 filter_set이라는 딕셔너리를 만들어 두고
filter_set = {
"search" : "name__icontains",
"new" : "is_new",
"sale" : "discount_percent__gte",
"category" : "category_id"
}
q = {filter_set.get(i):v for (i,v) in request.GET.items() if filter_set.get(i)}
위와 같이 딕셔너리를 생성하고
쿼리 파라미터 sale에 10 category에 3을 넣으면
{ "discount_percent__gte" : 10 , "category_id" : 3 }
이런 형태의 딕셔너리가 생성된다.
쿼리파라미터 값이 없으면 딕셔너리에 담기지 않고 Q객체를 통해서 쿼리셋을 합치지 않고 한번에 쿼리셋을 딕셔너리 형테로 만들 수 있다.
생성된 딕셔너리를
Product.objets.filter(**q)
언팩킹 해서 집어 넣으면
Product.objets.filter("discount_percent__gte"=10 , "category_id"=3)
이런 형태가 된다.
최종 코드를 보자면
filter_set = {
"search" : "name__icontains",
"new" : "is_new",
"sale" : "discount_percent__gte",
"category" : "category_id"
}
q = {filter_set.get(i):v for (i,v) in request.GET.items() if filter_set.get(i)}
products = Product.objects.filter(**q).prefetch_related('image_set')\
.annotate(avg_rate=Avg('comment__rate'),popular=Count("userproductlike", distinct=True),review_count=Count('comment',distinct=True))\
.order_by(order)[offset:offset+limit]
if문은 딕셔너리를 생성할 때 쓰이는 부분 1개만 남게 된다.
if filter_set.get(i)
파이써닉한 코드로 분기를 없앨 수 있었다.
근데 이렇게 했을 때 쿼리 파라미터에 이상한 값을 넣은 경우는 어떻게 처리했는지에 대한 과정은 https://kingofsiliconvalley.tistory.com/26?category=1046479이 포스팅을 참고하자
'DotFriends' 카테고리의 다른 글
⚫️Dotfriends 페이징 기능 쿼리 성능 향상 시키기 (0) | 2021.10.16 |
---|---|
📌 쿼리 파라미터 입력값에 따른 500에러 막기 (0) | 2021.09.24 |
⚫️ DotFriends 프로젝트 후기 (0) | 2021.09.12 |
제품 상세페이지 구현과 고민 01 (0) | 2021.09.05 |
prefetch_related로 성능 향상하기 (0) | 2021.09.05 |