일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- OSI7계층
- django westagram
- Jest
- 노드
- 자바스크립트
- 실행 컨텍스트
- typescript
- wecode
- async/await
- docker
- nodeJS
- westagram
- TypeError: this.boardRepository.createBoard is not a function
- 프로미스
- manytomanyfield
- crud2
- status code
- node
- pm2
- javascript
- CORS
- 트랜잭션
- rebase
- 스코프
- 호이스팅
- JWT
- Django
- on_delete
- 장고초기세팅
- bcrypt
- Today
- Total
될때까지
((TURTLE HOME)) 11일차 : ProductListView filtering refactoring 본문
((TURTLE HOME)) 11일차 : ProductListView filtering refactoring
랖니 2022. 7. 30. 12:00드디어 완성한 상품 리스트뷰.. 필터와 정렬이 이렇게 어려운 지 몰랐다. API하나 만드는데 시간이 이렇게 오래 걸릴 일인가 현타도 많이왔다. 나는 앞으로 나아가질 못하고 있는데 이미 다른 팀원들은 추가기능까지 도전하고 있고.. ㅜ_ㅜ 누구나 꽃피는 시기는 달라 어제의 나와 비교하자 파이팅!!! 멘탈을 다잡고 이제 완성된 코드를 리팩토링 해보자.
class ProductListView(View):
def get(self, request):
sort_by = request.GET.get('sort_by')
size = request.GET.get('size')
min_price = request.GET.get('min_price', 0)
max_price = request.GET.get('max_price')
limit = int(request.GET.get('limit', 20))
offset = int(request.GET.get('offset', 0))
sort_conditions = {
'high_price' : '-min_price',
'low_price' : 'min_price',
'newest' : '-created_at'
}
sort_field = sort_conditions.get(sort_by, 'created_at')
q = Q()
if size:
q &= Q(productoption__size__name = size)
q.add(Q(min_price__gte = min_price), q.AND)
if max_price :
q.add(Q(min_price__lt = max_price), q.AND)
products = Product.objects\
.annotate(min_price = Min('productoption__price'))\
.annotate(max_price = Max('productoption__price'))\
.filter(q).order_by(sort_field)[offset:offset+limit]
result = [{
'id' : product.id,
'name' : product.name,
'image_url': product.image_url,
'min_price': product.min_price,
'max_price': product.max_price
} for product in products]
return JsonResponse({'result':result}, status=200)
원래 정렬에 대한 분기처리(낮은 가격? 높은가격? 최신순?)를 if문을 반복해서 처리했었다. 그러다보니 계속 반복되는 if문 구조
if sort_by == '정렬조건':
정렬할 방법
맨 위의 코드처럼 딕셔너리로 만들어서 처리했더니, 반복되던 코드가 사라지고 훨씬 간결해졌다.
그렇다면 size, min_price, max_price도 딕셔너리로 처리할 수 있지않나!? 라는 생각이 들었고, 멘토님도 그렇게도 가능하다고 하셨다. 그렇다면 참을 수 없지 도전!!
딕셔너리 컴프리헨션 사용
class ProductListView(View):
def get(self, request):
sort_by = request.GET.get('sort_by')
limit = int(request.GET.get('limit', 20))
offset = int(request.GET.get('offset', 0))
sort_conditions = {
'high_price' : '-min_price',
'low_price' : 'min_price',
'newest' : '-created_at'
}
sort_field = sort_conditions.get(sort_by, 'created_at')
filter_conditions = {
'size' : 'productoption__size__name',
'min_price': 'min_price__gte',
'max_price': 'min_price__lt'
}
먼저 filter_conditions라는 빈 딕셔너리를 생성한다. 여기서 filter_conditions의 키값은 적용시킬 필터 조건이 된다.
프론트로부터 size, min_price, max_price의 키 이름으로 각각 키에 해당하는 벨류를 전달받을 예정이다.
url에 filter_conditions에 있는 키값이 있나 확인하고 있다면 각 키에 맞는 value값으로 가져오고 filter에 적용을 시켜야한다.
1. request.GET.items()를 사용하면 클라이언트로부터 요청을 받을 때 키와 벨류의 쌍을 얻을 수 있고 (key, value)형식으로 담긴다. 다만 뒤에 if조건식을 통해서 클라이언트러부터 전달받은 필터 이름이 filter_conditions에 있는 키값인 경우에만(size, min_price, max_price가 들어온 경우) 키, 벨류형식으로 담긴다.
/products?size=S?min_price=150000&max_price=300000 같은 경우
('size', 's'), ('min_price',150000), ('max_price', 30000) 처럼 담긴다
filter_field = {
filter_conditions.get(key):value
for (key, value) in request.GET.items()
if filter_conditions.get(key)
}
2. 반복문을 돌면서 클라이언트로부터 입력받은 조건들이 하나씩 (키,벨류) 형식으로 담겨있다. filter_conditions.get(key) : value를 실행하면서 이제는 filter_conditions의 벨류값이 왼쪽에 담기고(딕셔너리.get(key)는 key에 해당하는 벨류값을 반환) 클라이언트로부터 입력받은 value가 오른쪽에 담긴다.
/products?size=S?min_price=150000&max_price=300000 같은 경우
('size', 's'), ('min_price',150000), ('max_price', 30000) 처럼 담긴다.
첫번째 ('size', 's')의 경우 filter_conditions.get(size)의 결과값은 'productoption__size__name'이 된다.
value는 's'가 된다.
=> 'productoption__size__name' : 's'
두번째 ('min_price', 150000)의 경우 filter_conditions.get(min_price)의 결과값은 'min_price__gte'가 된다.
value는 150000이 된다.
=> 'min_price__get' : 150000
print(filter_field)
# {'productoption__size__name': 'double', 'min_price__gte': '150000', 'min_price__lt': '300000'}
3. 딕셔너리 앞에 별표를 2개 붙이면 ** 딕셔너리 언패킹이 일어난다. 그래서 키워드 인수로 사용할 수 있다.
products = Product.objects\
.annotate(min_price = Min('productoption__price'))\
.annotate(max_price = Max('productoption__price'))\
.filter(**filter_field).order_by(sort_field)[offset:offset+limit]
4. Product.objects.filter(productoption__size__name='double', min_price__gte='150000', min_price__lt='300000')가 되고, 상품 객체들 중 필터안의 조건에 해당하는 객체들을 찾아서 쿼리셋에 담아서 반환한다. (filter는 쿼리셋, get는 객체) 그럼 이제 리스트 컴프리헨션을 사용해서 각 객체에 맞는 값들을 넣어주면 코드 완성이다!
class ProductListView(View):
def get(self, request):
sort_by = request.GET.get('sort_by')
limit = int(request.GET.get('limit', 20))
offset = int(request.GET.get('offset', 0))
sort_conditions = {
'high_price' : '-min_price',
'low_price' : 'min_price',
'newest' : '-created_at'
}
sort_field = sort_conditions.get(sort_by, 'created_at')
filter_conditions = {
'size' : 'productoption__size__name',
'min_price': 'min_price__gte',
'max_price': 'min_price__lt'
}
filter_field = {
filter_conditions.get(key):value
for (key, value) in request.GET.items()
if filter_conditions.get(key)
}
products = Product.objects\
.annotate(min_price = Min('productoption__price'))\
.annotate(max_price = Max('productoption__price'))\
.filter(**filter_field).order_by(sort_field)[offset:offset+limit]
result = [{
'id' : product.id,
'name' : product.name,
'image_url': product.image_url,
'min_price': product.min_price,
'max_price': product.max_price
} for product in products]
return JsonResponse({'result':result}, status=200)
딕셔너리 컴프리헨션, 리스트 컴프리헨션은 마스터 한 것 같아서 뿌듯하다 😎
* 참고한 자료
https://velog.io/@adsf25/Django-%EB%8B%A4%EC%A4%91-%ED%95%84%ED%84%B0-%EC%A0%81%EC%9A%A9
'프로젝트 > wecode1차 : TURTLE-HOME' 카테고리의 다른 글
((TURTLE HOME)) 프로젝트 배포해보기 (0) | 2022.07.31 |
---|---|
((TURTLE HOME)) 위코드 1차 프로젝트 회고록 (0) | 2022.07.30 |
((TURTLE HOME)) 10일차 : ProductListView filtering(price) (0) | 2022.07.28 |
((TURTLE HOME)) 9일차 : ProductListView filtering(size) (0) | 2022.07.27 |
((TURTLE HOME)) 8일차 : ProductListView ordering refactoring (0) | 2022.07.27 |