(Django) 제품 filters 기능 구현하기
진행했던 프로젝트에서는 제품을 category, sub category별로 분류를 해놓아서 category -> sub category가 존재한다. 예) 의류 -> 여자 의류, 남자 의류 해당 프로젝트에서는 모든 제품별 , 카테로리별로 총 2가지의 메뉴바가 있다.
모든 제품별 filter기능은 form에서 직접 post요청을 보내 화면이 rendering되는 로직 으로 작성하였고 카테고리별 filter기능은 ajax를 통해 post요청을 보내 rendering 없는 로직 으로 작성하였다.
ajax를 이용한 로직을 기준으로 설명 할 예정이다 (사실 크게 다른건 없다. 단지 ajax로 데이터를 받았는지 아닌지 정도의 차이이다…^^ 물론 화면에 그려주는 태그는 따로 추가해주어야 하지만 ^^)
ajax로 요청 보내기 위해 submit태그에 onclick를 추가한다
form안에 있는 submit 버튼에 onclick함수를 추가해준다. 이때 해당 cateogry의 pk를 인수로 넣어 보낸다
<button class="product-filter-send" type="submit" onclick="filter_product('')">검색</button>
자바스크립트에서 Ajax를 통해 필요한 데이터를 post요청으로 보낸다
나는 항상 ajax를 사용할때 아래와 같은 틀로 사용한다. 바닐라 자바스크립트를 이용해 ajax를 사용하는 방법을 알고 싶다면 자바스크립트 부분을 확인해보면 된다. 간단하게 순서를 적어보자 1. view에서 필요한 데이터를 param에 딕셔너리 형태로 넣어준다
2. body에다 param데이터를 JSON.stringfy를 사용하여 담아준다
3. 요청을 보낼 url을 fetch에 작성해준다
function filter_product(category_pk) {
event.preventDefault();
categorySelect = document.querySelector('.category-detail-select');
priceStartSelect = document.querySelector('.product-price-start');
priceEndSelect = document.querySelector('.product-price-end');
productSortSelect = document.querySelector('.category-sort-select');
categoryOption = categorySelect.options[categorySelect.selectedIndex].value;
priceStart = priceStartSelect.options[priceStartSelect.selectedIndex].value;
priceEnd = priceEndSelect.options[priceEndSelect.selectedIndex].value;
productSort = productSortSelect.options[productSortSelect.selectedIndex].value;
let param = {
'sub-menu-pk': categoryOption,
'product-price-start': priceStart,
'product-price-end': priceEnd,
'product-sort': productSort,
'category_pk': category_pk
}
let csrfValue = document.getElementsByName("csrfmiddlewaretoken")[0].value;
console.log(category_pk)
// ajax통신
fetch("{% url 'product:select_detail' %}", {
method: 'POST',
headers: {
"X-CSRFToken": csrfValue,
"X-Requested-With": "XMLHttpRequest"
},
body: JSON.stringify(param),
}).then(function (response) {
return response.json()
}).then(function (data) {
}).catch((error) => {
console.log('error', error);
})
}
View에서 받은 데이터를 가지고 알맞게 가공한다
post 메서드에서 request.is_ajax()를 사용하여 ajax데이터를 받아온다. json.loads(request.body)를 사용하면 자바스크립트에서 body안에 넣었던 param데이터를 받아올수 있고 이를 get(‘data name’)으로 불러올 수 있다. 코드의 리팩토링이 필요해 보이지만 아래의 코드를 짜는데 굉장히 많은 시간을 들였다..^^
from django.db.models import Q, from django.db.models import Count 이 2가지를 import하여 제품을 filter 해주었다. category pk에 해당하는 category를 전부 가져오고 category_detail, 즉 상세상품을 선택한 경우에는 category_detail pk에 해당하는 제품을 가져온다.
이후 price 가격 범위가 price_from ~ price_to에 해당하는 제품을 가져온다. 이때 이 모든것을 import 받아온 Q로 필터해줄 수 있다.
class SelectDetailView(View):
def post(self, request, *args, **kwargs):
if request.is_ajax():
data = json.loads(request.body)
category = data.get('category_pk', None)
category_detail = data.get('sub-menu-pk', None)
price_from = data.get('product-price-start',0)
price_to = data.get('product-price-end', 10000000)
price_from = int(price_from)
price_to = int(price_to)
q = Q()
q &= Q(category = category)
if category_detail:
q &= Q(category_detail = category_detail)
q &= Q(price__range =(price_from, price_to))
if product_sort:
if product_sort == '1':
articles = Article.objects.filter(q).order_by('price')
elif product_sort == '2':
articles = Article.objects.filter(q).order_by('-price')
elif product_sort == '3':
articles = Article.objects.filter(q, is_deleted=False).annotate(like_count=Count('like__users')).order_by('-like_count','-created_at')
elif product_sort == '4':
articles = Article.objects.filter(q, is_deleted=False).annotate(review_count=Count('comment')).order_by('-review_count','-created_at')
else:
articles = Article.objects.filter(q).all()
comment_cnt = []
like_cnt = []
writer_profile = []
category_name = Category.objects.filter(pk = category).first().name
for article in articles:
comment_cnt.append(article.comment.all().count())
like_cnt.append(article.like.users.all().count())
writer_profile.append([article.writer.image, article.writer.nickname])
print(writer_profile)
articles = list(articles.values())
context = {'articles':articles, 'comment_cnt':comment_cnt,'like_cnt':like_cnt,'writer_image':writer_profile,'category_name':category_name}
return JsonResponse(context)
가격 filter
Q라는 기능을 import받아 쓰면 생각보다 아주아주 간단하게 내가 필요한 범위를 지정해서 그 안에 해당하는 제품들을 가져올 수 있다(난 저 기능을 쓰고 감동이였다.. ㅠㅠ)
대표적으로 가격을 filter해온 로직을 설명하려고 한다. 지정할 범위를 각각의 변수에 담는다. Q()의 빈객체를 변수 q에 담는다. price__range를 사용하여 price_from부터 price_to까지의 안에 존재하는 price를 q에 추가적으로 담아준다. 이후 Article에 조건을 넣어주었던 p변수를 넣어 조건에 맞는 값들만을 필터해서 출력한다
price_from = int(price_from)
price_to = int(price_to)
q = Q()
q &= Q(price__range =(price_from, price_to))
articles = Article.objects.filter(q).order_by('price')
자바스크립트로 데이터 전송
ajax를 사용하였기 때문에 나온 articles값들을 자바스크립트로 JsonResponse()를 통해 보내준다. 이때 articles = list(articles.values())
로 리스트 형식으로 보내주었다. 해당 데이터를 그냥 보내면 쿼리셋 문법이기 때문에 보내지지 않는다.
articles = list(articles.values())
context = {'articles':articles}
return JsonResponse(context)
이후 자바스크립트에서 innerHTML을 사용해 태그안에 필요한 값들을 넣어 실시간으로 제품이 필터되는것처럼 보이게 하였다. 해당 로직들은 then(function (data){} 이곳 안에다 써주면 된다.
then(function (data) {
// innerHTML() 로 데이터 추가
}