될때까지

((TURTLE HOME)) 5일차 : ProductDetailView 본문

프로젝트/wecode1차 : TURTLE-HOME

((TURTLE HOME)) 5일차 : ProductDetailView

랖니 2022. 7. 22. 20:06
728x90

첫번째 시도

  • 클라이언트로부터 product_id를 받는다. 전달된 product_id가 DB에 저장되어있는 Product 객체들의 id와 같은가 조회를 하고
  • 만약 Product객체들 중 일치하는 객체가 없다면 Product.DoesNotExist에러를 만나 except로 처리가 들어간다. 이때 400 을 에러코드로 정했다.
  • 400으로 처리한 이유는 400 Bad Request, 요청자체가 잘못된 접근이다는 판단이 들었다.
  • 하지만 멘토님의 리뷰는 리소스가 없는 경우에는 404 Not Found를 사용한다 하셨다.
  • 듣고보니 맞는 말, 요청한 Product 객체가 존재하지 않음 => 404 Not Found

처음 작성한 코드

두번째 시도(with 멘토님)

  • Product.objects.get(id=product_id)로 인해 이미 product_id가 존재하는 객체가 필터링 된 상황이다.
  • 그 상황에서 product.productoption_set.filter(product_id = product.id)라고 작성하면, 이미 product_id가 존재하는 상품 객체 중에, ProductOption테이블에서 product_id가 product.id와 일치하는 객체를 가져오라고 필터링을 하고 있다.
  • 필터링이 불필요한 상황이다 왜냐 이미 get에서 id=product_id로 해서 product를 걸렀으니까!
  • 그래서 위의 코드는 아래와 같이 수정이 된다.

수정한 코드

ORM에 대한 개념이 100% 확실하게 자리잡지 않은 것 같다. 첫날보다는 덜하지만 그래도 여전히 허우적대고 있다(?). 그리고 HTTP 상태 코드에 대한 개념도 아직 모자라다. 주말에 시간을 내서 공부하고 넘어가야겠다!!                                                          

 

* 일주일 지나고 다시 작성하는 리뷰

음? 어떻게 'size' : option.size.name으로 가져올 수 있지? option에는 size_id가 있지 size는 없는데? 역시 동기나 멘토님께 여쭤보고 해결했던 코드는 머리에 깊게 남지 않는다. 다시 지려밟아보자(?)

상품 옵션을 어떻게 option.size.name으로 가져올 수 있을까? 상품 옵션 테이블에는 product_id와 size_id만 있을텐데? 어떻게 .을 사용해서 타고 다른 테이블에 있는 외래키가 연결이 될까? 

정답은 내가 생성했던 ProductOption 테이블에 있다. ProductOption테이블에는 2개의 외래키가 연결되어 있는데, 이 필드의 이름은 'size'와 'product'로 만들었다. 그래서 ProductOption 테이블에는 'size'와 'price'라는 테이블 이름이 존재하는 것!' !!!

# products > models.py

class ProductOption(models.Model):
    product = models.ForeignKey('Product', on_delete=models.CASCADE)
    size    = models.ForeignKey('Size', on_delete=models.CASCADE)
    price   = models.DecimalField(max_digits=10, decimal_places=2)

    class Meta:
        db_table = 'products_options'

 

하지만 DB를 생성할 때는 해당 컬럼의 이름이 'size_id' , 'product_id'가 된다. 

 

class ProductDetailView(View):
    def get(self, request, product_id):

        try:
            product = Product.objects.get(id=product_id)   
            options = product.productoption_set.all()  
                
            result = { 
                'id'             : product.id,
                'name'           : product.name,
                'number'         : product.number,
                'description'    : product.description,
                'image_url'      : product.image_url,
                'sub_category_id': product.sub_category_id,
                'options'        : [{ 
                    'size'  : option.size.name,
                    'price' : option.price 
                } for option in options]
            }
            
            return JsonResponse({'result':result}, status=200)
            
        except Product.DoesNotExist:
            return JsonResponse({'message':'Product does not exist.'}, status=404)

product에는 id가 클라이언트로 부터 전달받은 product_id와 일치하는 객체 1개가 담겨있다. 그럼 해당 객체와 ProductOption테이블을 _set을 사용하여 연결할 수 있다(역참조 사용). 그럼 product에 담긴 객체의 product_id가 ProductOption테이블에 있는 product_id와 동일한 객체들을 모두 가져와서(all()) options에 담는다. 

리스트 컴프리헨션을 만나 product_id가 연결되어 있는 option 값 들 하나 하나(=가로값)의 size.name으로 해당 사이즈의 이름과 가격을 넣어주었다.  (예를 들어 a이불커버 - 싱글 : 5000, 더블 : 7000, 퀸 : 8000, 킹 : 9000 경우 반복문을 돌면서 처음에는 싱글 : 5000이 담기고, 두번째에는 더블 : 7000이 담긴다) 

정리하자면, 해당 테이블 안의 데이터 값 중 특정한 값을 가져오고 싶을때는 size_id와 같이 테이블의 컬럼이름값을 사용하여 가져올 수 있다. 그게 아니라 size테이블의 속성값(name)이 필요한 경우에는 dot notation을 써서 원하는 데이터를 가져올 수 있다.

728x90