다른 테이블 항목을 이용한 필터링 [20210911]

  이번에도 역시 현재 진행중인 클론 프로젝트 과정 중 어려웠던 부분에 대해 서술해 보려고 한다.
간략하게 먼저 언급하자면, 본인 테이블의 항목이 아닌 연결되어 있는 테이블의 항목으로 필터링을 하고자 할 때 사용할 수 있는 방법이다.

<목차>
1. 발단
2. 해결 과정
3. 해결책

1. 발단

문제의 발단에 대해 먼저 설명하고 넘어가자면 아래와 같다.

  진행중인 프로젝트의 모델링은 위의 사진과 같다. 우리가 하고자했던 필터링은 SubCategory에 있는 main_category_id를 사용하여 Product를 필터링 하는 것이었다.
그런데 자기 자신의 테이블에 있는 항목이 아닌 연관된(해당 테이블에서 Forgin Key를 갖는) 테이블의 항목을 사용하여 필터링을 하는 방법을 알 수가 없었다.

정말 별의 별 방법을 다 시도해 봤던 것 같다. 예를 들면, _set.all() 메서드를 사용하여 역참조도 활용해보고 Product.subcategory.main_category_id로 접근해 보기도 했다.

2. 해결과정

  도무지 방법을 찾지 못해, main_category_id로 필터링 하지 말고 Product에 있는 sub_category_id로 필터링 하는게 어떠냐는 의견이 나왔지만, 같이 프로젝트를 진행하고 있는 팀원 한 분이 그래도 조금 더 생각해보자는 의견을 제시했고 Django의 공식 문서를 뒤져보던 중 더블언더 스코어 (__)를 사용하면 해결이 될 것 같다는 의견이 제시되었고, 이와 관련하여 구글링을 하였다.
그러나, 우리가 활용하고자 하는 방법으로 사용한 예시를 찾지를 못했고 정말 계속 삽질을 했다. (거의 4~5시간 이것 때문에 3명이서 머리 맞대고 고민했었던 것 같다.)

3. 해결책

  결국 이런 저런 시도 끝에 해결 방법을 찾게 되었는데, 그 방법은 아래와 같다.

related_products = Product.objects.filter(sub_category__main_category_id = main_id)

해당 테이블(우리의 경우에는 Product 테이블)에서 Forign Key를 갖고있는 항목을 통해서 연결된 테이블에서 필터링 하고자 하는 항목 (우리의 경우에는 main_cateogry_id)를 더블 언더스코어(__)로 연결해주면 된다.

우리는 이와 같은 방식으로 연결되어 있는 테이블의 항목으로 필터링을 할 수 있었다.

  우리가 왜 상위 테이블에 있는 항목으로 필터링을 해야되었는지에 대해 간략하게 설명하자면, 제품의 상세페이지를 표시했을 때 연관된 상품을 추천해주는 서비스를 하고 싶었다. 우리의 경우에는 같은 main_category를 갖는 상품을 연관된 상품으로 추천해주기 위해서 이와 같은 시도를 했다.

참고로 우리가 진행했던 프로젝트의 상세페이지 코드는 아래와 같다.

class DetailView(View):
    def get(self, request, product_id):
        try:
            if not Product.objects.filter(id = product_id).exists():
                return JsonResponse({'MESSAGE' : 'NOT FOUND'}, status = 404)
            
            product = Product.objects.get(id = product_id)

            main_id          = product.sub_category.main_category_id
            related_products = Product.objects.filter(sub_category__main_category_id = main_id)

            if related_products.count() < 10:
                related_products = list(related_products)
            else:
                related_products = random.sample(list(related_products), 10)
            
            selected_products = [{
                    "image_url" : current_product.productimage_set.first().image_url,
                    "price"     : current_product.price,
                    "name"      : current_product.name,
                    } for current_product in related_products]

            return JsonResponse(
                {
                    'id'                : product.id,
                    'name'              : product.name,
                    'price'             : product.price,
                    'discount'          : product.discount,
                    'sales_unit'        : product.sales_unit,
                    'weight'            : product.weight,
                    'shipping_type'     : product.shipping_type,
                    'origin'            : product.origin,
                    'package_type'      : product.package_type,
                    'infomation'        : product.infomation,
                    'created_at'        : product.created_at,
                    'updated_at'        : product.updated_at,
                    'sub_category'      : product.sub_category.name,
                    'main_category'     : product.sub_category.main_category.name,
                    'menu'              : product.sub_category.main_category.menu.name,
                    'image_list'        : [img.image_url for img in product.productimage_set.all()],
                    'allergy_list'      : [allergies.name for allergies in product.allergy.all()],
                    'selected_products' : selected_products,
                }, status = 200
            )

        except KeyError:
            return JsonResponse({'MESSAGE' : 'KEY_ERROR'}, status = 400)

  특히 추천 상품을 10가지 정도로 노출하려고 했는데, 일부 제품의 경우에는 연관 상품이 10개가 되지 않을 경우를 대비하여 코드를 작성하였는데, 위의 코드에서 10~14번째 줄을 참고하면 될 것 같다.
추천 상품의 경우 for문을 사용하여 반복하였는데, List comprehension을 활용하였다. 아직은 List comprehension 작성하는 것이 서툴러서 같은 팀원 분이 작성한 것을 갖다 썼다. 그렇지만 전체적인 로직은 내가 짰는데 상세페이지에서 나름 마음에 드는 부분이었던 것 같다.

어제를 기준으로 1차 프로젝트가 끝났는데, 이에 관해서는 블로그 글을 추가로 올리려고 한다.

그럼 이만...

좋은 웹페이지 즐겨찾기