Project : WASH Korea [API]제품리스트 (1)

27286 단어 TILprojectTIL

위코드 1차 프로젝트


[API] 제품리스트

멘토님의 팁 !

어떻게 시작해야할지 감이 안잡힌다면

  1. 모든 상품 정보를 GET 해오는 코드 작성
  2. 필터링 으로 원하는 부분만 GET 해오는 코드 작성
  3. 쿼리파라미터로 필터링 처리
    + Q객체 사용법

순으로 작성해보면 쉬울 것이라고 하셨다

필요한 정보

: 제품 이미지, 태그, 이름, 서브이름, 가격 + 제품 id !


1. 전체 상품 정보를 GET

class ProductListView(View):
    def get(self, request):
        results = [{
	    "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.all()]
    
	return JsonResponse({'results' : results}, status = 200)

tagsimage는 둘 다 one to many 관계지만 현재 페이지에서 tags는 모든 내용이 필요한 반면, image는 메인 이미지 한 장만 있으면 되기에 서로 다른 방법을 사용했다

또한 여기서 product.images.first().url 역참조 관계인 Product에서 ProductImage를 정참조처럼 불러올 수 있는 이유는 related name 을 사용했기 때문이다

# products/models.py
class ProductImage(models.Model):
    url     = models.CharField(max_length=2000)
    product = models.ForeignKey('Product', on_delete=models.CASCADE, related_name='images')

    class Meta:
        db_table = 'product_images'

처음에는 for문을 사용해서 작성했지만 리스트 컴프리헨션을 사용하니 훨씬 깔끔해진 코드를 볼 수 있다



2. 필터링 사용

class ProductListView(View):
    def get(self, request):
        results = [{
	    "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(sub_category_id = 1)]
    
    	return JsonResponse({'results' : results}, status = 200)

.filter 메소드를 사용해서 서브카테고리 1번에 해당하는 제품만 불러올 수 있게 변경해 주었다



쿼리파라미터 / 패스파라미터

쿼리파라미터를 이용하기 전에 어떤 개념인지 먼저 알아보자

1. 쿼리파라미터

주로 데이터를 특정 조건으로 검색하거나 정렬하고자 할 때 활용되는 방식
Query Parameter으로 전송된 API에 대응하기 위해서 URL 패턴 정의를 따로 해주어야 하는 부분은 없다

대신 View 함수에서 해당 Parameter에 접근하기 위해서는 HTTP Method에 따라 request.GET 또는 request.POST로 QueryDict 객체를 호출해야 한다

QueryDict 객체는 Dictionary의 subclass로 key와 value형식으로 이루어져 있기 때문에, 전달되는 변수를 키값으로 조회해서 데이터를 처리하면 된다

2. 패스파라미터

커머스 사이트의 제품 상세 페이지에 대한 정보를 요청할 때 자주 사용

장고에서 Path Variable로 리소스에 대한 정보를 받아서 처리하기 위해서는 <>기호를 통해서 urlpattern을 정의해야 한다



3. 쿼리파라미터로 필터링

class ProductListView(View):
    def get(self, request):
	category     = request.GET['category']
	sub_category = request.GET.get('sub_category')
    
	if category:
	    Products = Product.objects.filter(sub_category_id__category_id = category)

	if sub_category:
	    Products = Product.objects.filter(sub_category_id = sub_category)
    
        results = [{
	    "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 Products]
    
	return JsonResponse({'results' : results}, status = 200)

카테고리와 서브카테고리로 각각 필터링을 주었다

여기서 category = request.GET['category'], sub_category = request.GET.get('sub_category')

두 가지 방법은 무엇이 다른 것일까 ?

답은 get() 메서드의 사용법에서 찾을 수 있다

get() 메서드는 키값이 딕셔너리 안에 있으면 밸류값을 리턴해주고 키값이 존재하지 않으면 디폴트값 None을 리턴한다 ( 물론 디폴트 값을 직접 정해줄 수 도 있다 )

따라서 현재 코드에선 category가 없으면 에러가 발생하고 sub_category는 없어도 된다 ( category 전체 제품 리스트 호출 )

러쉬코리아 홈페이지에서 확인을 해보면 카테고리별 전체 제품 보기는 있어도 러쉬의 모든 제품을 모든 페이지는 없기에 카테고리 코드는 category = request.GET['category'] 처럼 작성해서 항상 쿼리파라미터가 있도록 작성했다



4. 가격 순 정렬

제품리스트 페이지에는 다양한 정렬 기능이 존재한다

가격 높은 순, 낮은 순으로만 먼저 정렬 기능을 만들어 보자
쿼리파라미터에 price만 추가하면 된다

class ProductListView(View):
    def get(self, request):
	category     = request.GET['category']
	sub_category = request.GET.get('sub_category')
	price        = request.GET.get('price')
    
	if category:
	    Products = Product.objects.filter(sub_category_id__category_id = category)

	if sub_category:
	    Products = Product.objects.filter(sub_category_id = sub_category)
        
	if price:
	    Products = Products.order_by(price)
    
        results = [{
	    "id"            : product.id,
	    "name"          : product.name,
            "sub_name"      : product.sub_name,
	    "price"         : int(product.price),
	    "tags"          : [tag.name for tag in product.t정ags.all()],
	    "product_image" : product.images.first().url    
	} for product in Products]
    
	return JsonResponse({'results' : results}, status = 200)

👏 멘토님 리뷰 👏

현재 방식으로 작성할 경우 정렬기능이 추가될 때 마다 계속 쿼리파라미터와 if문을 추가로 만들어줘야 한다

정렬될 내용을 묶음으로 만드는 것이 훨씬 깔끔하게 보이고 스스로가 편하다

ex.

class ProductListView(View):
    def get(self, request):
	sorting = request.GET.get('sort', 'id')
...
        sort = {
	    "id"     : "id"
	    "price"  : "price",
	    "-price" : "-price",
        }

너무나 당연한 말이다 러쉬홈페이지만 봐도 추천을 기본 순으로 본다면 4개의 정렬이 만들어져야 한다

바로 반영해서 코드를 수정해 주었다

class ProductListView(View):
    def get(self, request):
	category     = request.GET['category']
	sub_category = request.GET.get('sub_category')
	sorting      = request.GET.get('sort', 'id')
   
	if category:
	    Products = Product.objects.filter(sub_category_id__category_id = category)

	if sub_category:
	    Products = Product.objects.filter(sub_category_id = sub_category)
      
	sort = {
            "id"     : "id",
            "price"  : "price",
            "-price" : "-price",
        }
   
        results = [{
	    "id"            : product.id,
	    "name"          : product.name,
            "sub_name"      : product.sub_name,
	    "price"         : int(product.price),
	    "tags"          : [tag.name for tag in product.t정ags.all()],
	    "product_image" : product.images.first().url    
	} for product in Products]
   
	return JsonResponse({'results' : results}, status = 200)

그러면 Q객체 는 언제 쓰는 걸까 ???

다음 글에서 만나봅시다 !!!

좋은 웹페이지 즐겨찾기