쉽게 먼저 얘기하면
ProductListView
에서Q()
=Product.objects.all()
이다
q = Q()
선언 후&= , |=
을 통해 필터링을 각각의 조건에 교집합, 합집합을 적용할 수 있다
→ 1. 쿼리파라미터로 request.GET.get
을 이용할 시 기본 url 을 통해 호출을 시도하면 for product in Product.objects.filter(sub_category_id__category_id = category)
와 같은 방식으로 필터링 했을 때 (거의 없겠지만...) 필터 값이 None
값으로 넘어가기 때문에 빈 값이 반환된다
이 때, q = Q()
를 먼저 선언하고 Product.objects.filter(q)
로 하면 기본 url 일 때 전체 값을 받아올 수 있다
→ 2. & , |
을 통해 복잡한 필터를 걸 수 있다 !! ( 여기가 핵심 ! )
각각의 제품리스트 페이지에는 카테고리 (전체) , 서브카테고리 ( 솝 , 샤워 젤 & 젤리 , 등 .. ) 마다 각각의 헤더 이미지와 설명을 가지고 있다
이것을 현재의
results
에 같이 넣어서 정보를 준다면 제품의 개수만큼 반복된다
따라서 반복문을 돌릴 제품리스트와 헤더는 나눠주는 것이 좋다if category: products = Product.objects.filter(sub_category_id__category_id = category) products_header = Category.objects.get(id=category) if sub_category: products = Product.objects.filter(sub_category_id = sub_category) products_header = SubCategory.objects.get(id=sub_category) results = { "data" : [{ "id" : product.id, "name" : product.name, "sub_name" : product.sub_name, "price" : int(product.price), 'tags' : [tag.name for tag in product.tags.all()], "product_image" : product.images.first().url } for product in Product.objects.filter(q).order_by(sort[sorting])], "products_header" : { "name" : products_header.name, "image" : products_header.image, "description" : products_header.description } } return JsonResponse({'results' : results}, status = 200)
이렇게 따로 뺄 수 있다 ! 물론 프론트 쪽과 미리 데이터가 어떻게 넘어가는지 얘기 잘 하자.. 꼭 꼭
미리 안하면 통신할 때 난리난다 물론 프론트가..
사진을 확인해보면 러쉬페이지의 경우 내비게이션 바에서 카테고리 / 서브카테고리 전체 내용을 필요로 하고 밑에 제품리스트 부분의 목록에선 하나의 카테고리와 그에 해당하는 서브카테고리 전체 내용이 필요하다
반복문을 통해 카테고리와 서브카테고리 전체를 한 번에 넘겨주고 프론트에서 각각 뽑아서 사용할 수 도 있겠지만 그럴 경우 제품리스트 부분의 목록에선 필요없는 데이터를 더 많이 받게 된다
그래서
CategoryListView
와CategoryView
로 카테고리 전체의 데이터를 주는 것과 선택한 카테고리에 대해서만 데이터를 주는 것을 구분하였다
CategoryListView
의 경우 전체의 이름과 해당하는 id만 필요하다class CategoryListView(View): def get(self, request): result = [{ "category_id" : category.id, "category_name" : category.name, "sub_categories" : [{ "sub_category_id" : sub_category.id, "sub_category_name" : sub_category.name }for sub_category in category.subcategory_set.all()] }for category in Category.objects.all()] return JsonResponse({'results' : result}, status = 200)
CategoryView
의 경우에는 이름과 해당하는 id, 추가로 필터링 된 제품의 개수가 필요하다
개수는.count()
메소드를 사용했다이 때 !!!!! 중요한 점은 전체를 불러오는 것이 아닌 필터를 통해 일부를 가져오는 것이기 때문에 필터링 했을 때 해당하는 값이 없을 경우 에러메시지를 줘야한다
그것이 바로 'DOES NOT EXISTS'
꼭꼭꼭 적자class CategoryView(View): def get(self, request, category_id): if not Category.objects.filter(id=category_id).exists(): return JsonResponse({'message' : 'DOES NOT EXISTS'}, status=404) category = Category.objects.get(id=category_id) result = { "category_id" : category.id, "category" : category.name, "count" : Product.objects.filter(sub_category_id__category_id=category).count(), "subcategories" : [{ "sub_category_id" : sub_category.id, "name" : sub_category.name, "count" : sub_category.product_set.count() }for sub_category in category.subcategory_set.all()] } return JsonResponse({'results' : result}, status = 200)
필터링에 대해 열심히 공부 / 코드 작성을보니 조금만 더 응용하면 검색기능으로 쓸 수 있을 것 같아서 검색 기능도 추가해보았다
여기서는 검색에 대한 필터링으로 or
가 필요하기에 Q객체
를 사용해보았다
검색 페이지에서는 header로 이미지, 설명은 없고 입력된 검색어와 해당하는 제품의 개수를 포함해야 한다
검색 결과로 나오는 제품은 기존의 ProductListView
와 동일하다
class SearchView(View):
def get(self, request):
search_word = request.GET.get('search_word')
sorting = request.GET.get('sort', 'id')
q = Q()
if search_word:
q = Q(sub_category__category__name__contains = search_word)|\
Q(sub_category__name__contains = search_word)|\
Q(name__contains = search_word)|\
Q(tags__name__contains = search_word)
sort = {
"price" : "price",
"-price" : "-price",
"id" : "id"
}
results = {
"data" : [{
"id" : product.id,
"name" : product.name,
"price" : int(product.price),
"sub_name" : product.sub_name,
'tags' : [tag.name for tag in product.tags.all()],
"product_image" : [image.url for image in product.images.all()]
} for product in Product.objects.filter(q).distinct().order_by(sort[sorting])],
"search_hedaline" : {
"word" : search_word,
"count" : Product.objects.filter(q).distinct().order_by(sort[sorting]).count()
}
}
return JsonResponse({'results' : results}, status = 200)
여기서 .distinct()
라는 메소드를 쓴 이유는 현재 각각의 제품마다 tags
가 여러 개 달려있기 때문에 태그의 개수만큼 출력되는 문제가 생겨서다
터미널 창으로 확인해보자
http -v GET 127.0.0.1:8000/products/search search_word==펌킨
/ .distinct()
없이
http -v GET 127.0.0.1:8000/products/search search_word==펌킨
/ .distinct()
포함
필터링 하는 곳은 'DOES NOT EXISTS' / 키 에러가 나는 곳은 'KEY_ERROR' 을 넣어주자
이왕 넣는거 if문
말고 try / except
를 사용해서 넣어주면 보기 더 좋다
이상으로 ProductListView
는 마무리 !!!!