@login_required
def post(self, request, product_id) :
try :
user = request.user
if user.role_id != RoleID.ADMIN.value :
return JsonResponse({'message' : 'PERMISSION_DENIED'}, status=403)
with transaction.atomic() :
data = json.loads(request.body)
thumbnail_url = data.get('thumbnail', None)
name = data.get('name')
sales = data.get('sales')
product = Product.objects.select_related('thumbnail').get(id=product_id)
if thumbnail_url :
thumbnail = product.thumbnail_set.get()
thumbnail.url = thumbnail_url
thumbnail.updated_at = timezone.now()
thumbnail.save()
if sales :
product.price = F('price') * (100-int(sales)) / 100
product.save()
if name :
product.name = name
product.save()
product.updated_at = timezone.now()
return JsonResponse({'message' : 'SUCCESS'}, status=201)
except Product.DoesNotExist :
return JsonResponse({'message' : 'PRODUCT_DOES_NOT_EXIST'}, status=400)
우선, 상품수정은 일반유저가 할 수 없기 때문에 로그인 유저의 토큰을 통해 등급을 확인합니다.
일반 유저면 403 Forbidden
에러가 발생합니다.
수정의 경우, PATCH
와 PUT
이 있지만 POST
를 많이 사용한다고 하여 POST
를 선택했습니다.
Product
와 Thumbnail
의 경우, Product
가 Thumbnail
을 역참조하기 때문에
prefetch_related
를 쓰려고 했으나, OneToOneField
에서 역참조 관계는
select_related
를 사용하기 때문에 select_related
를 사용했습니다.
product_id
를 통해 thumbnail
에 접근할 때, 1대1 관계기 때문에 역참조관계에서
쿼리셋이 아닌 Thumbnail
객체를 가져와야 했고, 그래서 product.thumbnail_set.get()
을
사용했습니다.
수정날짜의 경우, auto_now=True
를 설정하지 않아서 수정시간을 업데이트했습니다.
auto_now=True
를 하면 데이터 생성할 때 updated_at
에 created_at
날짜가 들어가기 때문입니다.
그리고 F
객체를 써봤는데, 원래 가격이야 수정된 가격을 입력하면 되지만
F
객체 사용을 위해 할인율을 입력하는거로 일부로 코드 작성했습니다.
일반 가격을 입력하면 product.price
에 직접 입력하여 장고가 수행하는 과정이 하나 더 늘어나고,
F
객체를 사용하면 DB의 값에서 작업을 수행하기 때문에 쿼리수를 줄일 수 있습니다.
예를 들어 쿼리를 가져오면 위에가 F객체, 아래가 일반 값 저장인데
product.price = product.price * (100-int(sales)) / 100
"price" = (("products"."price" * 70) / 100) WHERE "products"."id" = 1; args=('2021-12-09 19:09:51.685829', 6, 'LIMITED EDITION 올 블랙 코트', 70, 100, 1)
"price" = 82320 WHERE "products"."id" = 1; args=('2021-12-09 19:09:51.685829', 6, 'LIMITED EDITION 올 블랙 코트', 82320, 1)
이렇게 DB에서 연산처리를 하게 되는 것입니다.
이걸 통해 경쟁조건(race condition)을 피할 수 있는데, 조회수를 예를 들면
- 현재 조회수(page.read_count = 1)
- A 스레드 조회(page.read_count = 1 + 1 )
- B 스레드 조회(page.read_count = 1 + 1 )
원래는 A, B에서 클릭했으니 3이 되어야 하는데 파이썬 기준으로 하다 보니
경쟁 조건이 발생합니다. 타이밍이나 순서에 따라 결과값에 영향을 주기 때문입니다.
하지만 F
객체를 쓰게 된다면, A 스레드에서 DB 기준으로 +1 하므로 2가 되고
B 스레드에서 DB 기준으로 +1 하기 때문에 3이됩니다.
수정을 기준으로 기본적으로 구현할 사항들은 모두 종료되었습니다.
분명 모르는 부분이 아직 많기 때문에 리팩토링할 부분이 반드시 존재하고
꾸준한 공부를 통해 더 발전된 코드를 작성할 예정입니다.