1차 프로젝트 8일차

1. 스탠딩 미팅 :

  1. 프론트 / 백엔드 일정 공유 : 주로 장바구니, 찜하기, 주문하기 위주로 실시. UI는 많이 진행 되었음. ; 케로셀도 진행중
  2. 오늘 할 일 :
  • 백엔드 : 장바구니, 찜하기 완료하기 + 상품 상세 정보 출력 view 마무리하기 + 데코레이터 적용하기
  • 그 외 : 프론트와 맞춰보기, 큰 스크럼 완성(일단 추가 기능보다는 기존 완성된 기능 완성해서 맞추기) -> 한 판 돌려서 뭐가 모자란지 확인하기

2. 리뷰

  1. 상품 상세 정보 출력 뷰는 금방 마무리
  2. 위시리스트(찜하기) 리뷰
  • 위시리스트는 유저에 가까운 것이므로 유저로 뷰 옮기기->RESTful
  • create는 201쓰기
  • list, dict 보다는 [], {} 가 속도 더 빠름
  • 유저를 하드코딩으로 박지 말고 데코레이터로 깐 토큰 정보로 확인시키기

3. 위시리스트 뷰를 통해 배운 4가지 내용

  1. 토큰 깐 정보 for 문으로 확인하기({}로 데이터 가져오기) + 토큰 값 가져온 것 확인하기
class WishListView(View):
    @auth_check
    # 토큰
    @transaction.atomic
    def post(self, request):
        try:
            results = json.loads(request.body)['results']
            딕셔너리 형태로 가져온 프론트에서 받은 내용
            user_id = request.user.id
            # 토큰 까기
            for result in results:
                product_id              = result['product_id']
                quantity                = result['quantity']
                product_option_id       = result['product_option_id']
                product_option_quantity = result['product_option_quantity']
                # for loop으로 딕셔너리 내용 까기
  • 기본적으로 프론트에서 제이슨으로 받아올 때 어떻게 들어오는지 생각해야한다.
{"results": [
{"product_id": 1, 
"quantity" :4, 
"product_option_id": "", 
"product_option_quantity":0
}]
}
  • 토큰을 깠을 때 나오는 정보가 유저의 정보(보통 id)가 무엇인지 이해해야 한다.

  • 토큰을 가져오는 데코레이터의 구조를 이해한다.

  • 우리 조는 결과가 result라는 딕셔너리 형태로 온다. 따라서 이를 바디에서 가져오는 방식을 이해해야 한다.(대괄호)

  • 이를 for 문을 돌려서 한 번 풀어준다. 그리고 푼 정보(프론트가 전달해준 정보)를 result와 연동하여 정리한다.

  • 우리조의 경우 옵션이 있는 경우와 없는 경우를 if문으로 나눠서 관리했다.

                if product_option_id:
                # 만일 product_option_id가 있으면
                    wishlist, is_created = 
                    # 언패킹 
                    WishList.objects.get_or_create(
                        product_id        = product_id,
                        product_option_id = product_option_id,
                        user_id           = user_id,
                        defaults          = {'quantity': product_option_quantity}
                        # get_or_create 문법

옵션이 있다고만 가정한 것을 보고 위에서 정의해준 내용을 바탕으로 create해주면 된다.

  1. get_or_create와 unique_together
  • 여기서 의문이 드는 것이 get_or_create와 default다. 대체 이건 뭘까?
    저번 블로그에 썼듯 get_or_create는 있으면 get, 없으면 create하는 존재다. 근데 default는 왜 쓸까?

만일 컬럼 한 줄을 다 채우는 식을 하나 썼다 치자. 그리고 이를 양말이라 치자. 근데 만일 같은 사람이 같은 양말을 같은 옵션으로 다른 갯수로 다시 위시리스트에 담았다. 그러면 컬럼이 또 생기는게 좋을까 아님 갯수만 올라가는게 좋을까? 당연히 후자다. 이 때 갯수를 디폴트로 놓고 나머지 값들로 이 컬럼을 판단하는 과정이 get_or_create다.

unique_together는? 이미 그 관계가 모델링되어 있다. 따라서 이 모델링에 맞춰서 디폴트 값이 갯수가 되어야 한다는 것이다.

    class Meta:
        db_table = 'wishlist'
        unique_together = ('product', 'user', 'product_option')
  1. 이어서 is_create와 언패킹, 데이터에 직접 데이터 더했을 때 저장하기
                    if not is_created:
                    # False
                        wishlist.quantity += product_option_quantity
                        wishlist.save()
  • get_or_create로 데이터를 만들 때 True면 데이터가 만들어지고, False면 데이터를 가져온다. 그래서 not~ 즉 데이터가 이미 컬럼에 있는 상황이면 당연히 갯수를 더 해줘야 한다. 근데 데이터베이스에 장고 내용을 바로 더해줘야 하기 때문에 어디서 뭘 가져왔는지, 어떤 데이터를 더하는지 위 아래로 정확히 데이터를 봐야 한다. 지금은 위시리스트에서 가져온 퀀티티가 데이터베이스에 있는 값이고 오른쪽에 있는 값이 프론트가 입력하는 값이라 이렇게 했는데 변수 설정, 데이터베이스 설정에 따라 이는 달라질 수 있다.
    중요한 것은 save()를 해줘야 한다는 것이다.
  1. 리스트 컴프리헨션
  • 고민한 점 : 어떻게 시작할지 몰랐다는 점. 그리고 형태는 갖췄으나 데이터를 잘 못 가져온다는 것. -> 어디서 뭘 가져오는지 감을 못 잡음.
    @auth_check
    def get(self, request):        
        wishlists = WishList.objects.filter(user_id=request.user.id)
        # 토큰 통한 하드코딩 방지
        try:
            results = [{
                'point'             : wishlist.user.point,                
                'quantity'          : wishlist.quantity,
                'user_id'           : wishlist.user.id,
                'product_option_id' : wishlist.product_option.id,
                'product_id'        : wishlist.product_id,
                'product_name'      : wishlist.product.name,
                'product_thumnail'  : wishlist.product.thumbnail_image_url,
                'product_price'     : wishlist.product.price if not wishlist.product_option\
                                      else wishlist.product.price + wishlist.product_option.additional_price ,
                'total_price'       : wishlist.quantity * wishlist.product.price if not wishlist.product_option\
                                      else wishlist.quantity * (wishlist.product.price + wishlist.product_option.additional_price),
                'product_option_classification': wishlist.product_option.option.classification,
                'product_option_name'          : wishlist.product_option.option.name
                } for wishlist in wishlists]
            return JsonResponse({'result' : results}, status=200)
  • 리스트 컴프리헨션 : 쉽게 리스트, 혹은 딕셔너리를 만들 수 있는 유형.
  • 여기서는 위시리스트에서 가져오는 모든 자료이므로 위시리스트에서 가져오는 것으로 해야 함.
  • 한 줄로 if else 쓰는 것 : 옵션 여부에 따라 토탈 가격이 달라진다. 옵션에 가격이 붙어있기 때문.
>>>  [x for x in range(1, 10+1) if x % 2 == 0]
[2, 4, 6, 8, 10]
  • 여기서 확인할 점 : 데이터 가져올 때 막히는 경우 어느 경로에서 가져오는지 생각하기.(아래 멘토님 과제)

3.1 최종 발표 전 수정 사항

-> 무조건 적으로 for loop을 쓰지 말란게 아니라 리스트 컴프리핸션이 되거나, 불필요한 변수를 for 로 가져오는 것을 지양하라는 것. 여기서는 위의 로직대로라면 한 개의 상품만 위시리스트에 담을 수 있으므로 상품을 다 가져와서 for loop처리 해주는 것이 좋다.

4. 멘토님 과제

정참조, 역참조 및 json으로 데이터 출력에 대한 이해가 부족하여 과제가 부여되었고 두 문제가 출제 되었다. 하나는 정참조를 할 수 있는지, 하나는 역참조가 무엇인지 아는지였다.

  1. AQUARY의 관계를 보고 다음 출력이 나오는 코드를 생각해보기

{
   "results":[
      {
         "name":"웅이",
         "breed":"푸들",
         "age":7,
         "owner":"손승현"
      },
      {
         "name":"짱아",
         "breed":"페키니즈",
         "age":3,
         "owner":"손승현"
      },
      {
         "name":"삐삐",
         "breed":"요크셔테리어",
         "age":8,
         "owner":"박지훈"
      },
      {
         "name":"버블",
         "breed":"푸들",
         "age":2,
         "owner":"강경훈"
      },
      {
         "name":"왕자",
         "breed":"진돗개",
         "age":1,
         "owner":"김순태"
      }
   ]
}
  • 고민 :
    일단 사람과 동물의 1:다 관계, 유저 아이디를 가져와 필터링하는 것으로 생각
  • 해결 : 단순히 Dog.name, Dog.breed... 처럼 단순하게 가져오는 것이었고 사람의 경우는 Dog.User.name 등으로 가볍게 가져오면 되는 문제였다.
    -> 인스턴스에서 어트리뷰트 가져오는 것 생각해보기
  1. 역참조를 아는지.
[
   {
      "name":"손승현",
      "email":"jamessoun93@gmail.com",
      "age":17,
      "dogs":[
         {
            "name":"웅이",
            "breed":"푸들",
            "age":7
         },
         {
            "name":"짱아",
            "breed":"페키니즈",
            "age":3
         }
      ]
   },
   {
      "name":"박지훈",
      "email":"ulfrid@gmail.com",
      "age":19,
      "dogs":[
         {
            "name":"삐삐",
            "breed":"요크셔테리어",
            "age":8
         }
      ]
   },
   {
      "name":"강경훈",
      "email":"kyunghun@gmail.com",
      "age":18,
      "dogs":[
         {
            "name":"버블",
            "breed":"푸들",
            "age":2
         }
      ]
   },
   {
      "name":"김순태",
      "email":"kst@gmail.com",
      "age":17,
      "dogs":[
         {
            "name":"왕자",
            "breed":"진돗개",
            "age":1
         }
      ]
   }
  • 마찬가지로 유저.name, 유저.email... 식으로 하다가 강아지가 역참조 되므로 User.dog_set.all()로 가져온다.
  • 누가 소문자 : 참조 쓰는 쪽.
  • 추가 고민 : 그럼 가져온 쿼리셋에 정보를 가져오는 법은?
  • 인스턴스를 가져올 수 있게 first() 등을 처리해주고 여기에 .name 등 어트리뷰트 해준다.

이건 내가 하는 프로젝트 product와 product_image 예시

In [51]: p8.productimage_set.all()
Out[51]: <QuerySet [<ProductImage: ProductImage object (1)>]>

In [52]: p8.productimage_set.all().first()
Out[52]: <ProductImage: ProductImage object (1)>

In [53]: p8.productimage_set.all().first().image_url
Out[53]: 'https://endlessfight1.openhost.cafe24.com/web/product/2018/haguesock.jpg'

정/역참조 참고 블로그

  1. 쉘 두둘기다가 추가로 알게 된 사항들
  • 역참조 때 정참조 때 for 문을 쓰는 이유(역참조는 이중 for)
    -> 직접 까보면 안다. 단순히 Product.objects.all()하면 오브젝트 형태가 나오는데 이를 values()로 가져오면 {} 형태로 나온다. 그래서 위에 이중 {}은 이중 for문이 사용되는 것
  • 이를 리스트 컴프리핸션으로 하면 간단해진다고 한다.

5. 프론트엔드와 찜하기 위시리스트 맞춰보기

  1. 프론트와 맞추기 전 확인사항
  • get(불러오기)

  • post(항목 같으면 갯수만 올라가게)

  • 프론트와 맞춘 결과

  1. 팀원들이 맞추고 있는 내용
  • 장바구니
  • 카테고리별 페이지 물건 출력
  1. 내일 할 일 :
  • 맞추고 있는 내용 (장바구니, 카테고리별 페이지 물건 출력, 프론트는 머지 후 로그인) 한 사이클 돌기
  • 추가구현(쿠폰) 만들기
  • LOVE PM님 상담

6. 추가구현 - 쿠폰

  1. 쿠폰 만들기(user/views.py)에 만든 초안
class CouponView(View):
    def post(self, request):
        try:
            data           = json.loads(request.body)
            name           = data['name']
            discount_price = data['discount_price']
            issue_date     = data['issue_date']
            expire_date    = data['expire_date']
            
            coupons = Coupon.objects.create(
                name = name,
                discount_price = discount_price,
                issue_date = issue_date,
                expire_date = expire_date
            )
            return JsonResponse({'message' : 'SUCCESS'}, status=201)
        except KeyError:
            return JsonResponse({'message' : 'KEY_ERROR'}, status=400)
        except JSONDecodeError:
            return JsonResponse({'message' : 'JSON_DECODE_ERROR'}, status=400)

로그인이나 회원가입 참고하여 간단히 만듦.

  1. 쿠폰과 유저 관계 Aquary table과 같이 만들어서 컬럼 채우기
 def post(self, request):
 # 데이터 집어 넣는 것이니 post
        try:
            data = json.loads(request.body)
            coupon = Coupon.objects.get(id=data['coupon_id']).id
            user = User.objects.get(id=data['user_id']).id
            quantity = data['quantity']
# 각 아이디 및 수량 받아 옴.
            
            user_coupon, is_created = UserCoupon.objects.get_or_create(
            coupon_id = coupon,
            user_id = user,
            defaults = {'quantity' : quantity}
            )
            if not is_created:
                user_coupon.quantity += data['quantity']
                user_coupon.save()
                # 마찬가지로 같은 쿠폰을 받을 경우 수량을 늘려야 함.
            return JsonResponse({'message' : 'SUCCESS'}, status=201)
        except KeyError:
            return JsonResponse({'message' : 'KEY_ERROR'}, status=400)
        except JSONDecodeError:
            return JsonResponse({'message' : 'JSON_DECODE_ERROR'}, status=400)

이후 구현해야할 사항 : 유저와의 관계 보강(단순히 관계만 맺음 / 서브 카테고리와의 관계

유저에게 무료 금액 쿠폰을 가입할 때 부여하는 방식 및 서브 카테고리마다 쿠폰이 존재하는 로직이 필요.

profile
커피 내리고 향 맡는거 좋아해요. 이것 저것 공부합니다.

0개의 댓글

관련 채용 정보