[Django] 다중 필터 적용

재운·2021년 4월 20일
6

다중 필터❓

다중 필터에 대한 고민은 커머스 사이트(Discovery Expedition) 클론 코딩 프로젝트 진행중 커스텀 필터를 구현하다가 하게되었다.

이 필터의 핵심은

(사이즈 중복 선택) & (색상 중복 선택) & (가격 범위) 인데, 내가 봤을때는 필터의 모든것을 담고 있지 않나 생각이 든다.

직관적으로 생각해보면

Product.objects.filter(필터 내용)

'필터내용'에 한방에 필터를 처리하는 코드를 넣고 었고, 그렇게해서 생각난 것이 장고의 Q객체이다. 그런데 처음 생각할 때는,

Product.objects.filter((Q(사이즈1) or Q(사이즈2) or ...)
			& (Q(색깔1) or Q(색깔2) or ...)
            		& Q(가격 범위))

와 같이 Q객체를 사용하려고 했지만, 그렇게 되면 옵션이 추가될때마다 Q객체를 filter 안에 집어넣어줘야하고 + 아무 옵션이 없을때 전체 상품을 보여주지 못한다.

그러다 구선생님의 도움하에 q = Q() 와 같이 빈(empty) Q객체를 사용하면 Q안에 조건이 들어가지 않을시 All 조건이 될 수 있고,
q &= Q(필터내용)으로 Q객체를 추가할 수 있다.

이를 토대로 위의 필터바 기능을 구현해보면

size               = request.GET.getlist('size', None)
color              = request.GET.getlist('color', None)
price_upper_range  = request.GET.get('PriceUpper', 1000000)
price_lower_range  = request.GET.get('PriceLower', 0)

q=Q()
if category:
	q &= Q(category_id = category)
if color:
    q &= Q(color_name__in = color)
if size:
    q &= Q(size__name__in = size)
    
q &= Q(price__range = (price_lower_range,price_upper_range))
            
products = Product.objects.filter(q).order_by(ordering)

size와 color는 여러 옵션들이 선택되어서 들어오기때문에 getlist로 가져오고 리스트안에 담긴다. 이 때문에 Q객체 안에 __in 을 사용하게된다.

__in 을 사용법
size가 230,240,250인 데이터를 모두 찾으려면
Product.objects.filter(id__
in=[230,240,250]) 와 같이 사용하면 된다.

추가적인 필터링 방법

장고 객체 필터 방법을 찾아보던중 딕셔너리를 활용한 방법이 있어서 추가적으로 구현을 해보았다.

filter_options = {
            'color'            : 'color__name__in',
            'size'             : 'size__name__in',
            'price_lower_range': 'price__gte',
            'price_upper_range': 'price__lte',
        }

        filter_set = {
            filter_categories.get(key) : value for (key, value) in dict(request.GET).items() if filter_options.get(key)
        }
        
        products = Product.objects.filter(**filter_set).distinct() 

이 방식은 대신 key값에 대한 value 가 모두 리스트로 들어오기때문에 "__in"을 붙여줘야한다.

필터값이 전해지는 로직을 설명하자면,

request에서 Query string으로 color=red&color=blue&color=green 으로 들어오면

#print(request.GET)
<QueryDict: {'color': ['red', 'blue', 'green']}>

위와 같이 들어오게 되고, 아래와 같이 적용된다.

filter_set={'color_name__in':['red', 'blue', 'green']}

#위와 같이 인자가 들어오게 되고 filter에는 아래와 같이 적용된다.

Product.objects.filter(color_name__in=['red', 'blue', 'green'])

또한 마지막에 **filter_set으로 사용했는데, 이것이 딕셔너리 언패킹이다.

*출처 : 파이썬 코딩도장

참조)
https://velog.io/@suasue/Django-%ED%95%84%ED%84%B0%EB%A7%81-%EB%BD%80%EA%B0%9C%EA%B8%B0-if%EB%AC%B8-%EB%94%95%EC%85%94%EB%84%88%EB%A6%AC-Q-%EA%B0%9D%EC%B2%B4-%ED%99%9C%EC%9A%A9

profile
Life is memory

1개의 댓글

comment-user-thumbnail
2021년 7월 16일

자세하고 친절한 글 감사합니다

답글 달기