Section 1. 커스텀 액션 추가
1. 액션 기능
- 목록 페이지에서 행 체크박스 체크 후 원하는 액션 실행하는 기능
2. admin.py 수정
- queryset 으로 체크박스에 체크한 행들이 넘어옴
- refund () 함수 선언 후 actions 에 지정
- 주문 환불 시 Product 의 quantity 값을 수정하는 DB 작업이므로 transaction.atomic() 사용
- queryset 의 모델(테이블)을 추출하기 위해 ContentType.objects.get_for_model() 사용
(from django.contrib.contenttypes.models import ContentType)
qs = queryset.filter(~Q(status='환불')) # status 속성이 '환불'이 아닌 행들만 추출
ct = ContentType.objects.get_for_model(queryset.model) # queryset의 model(테이블)을 추출 -> '주문' 추출됨
obj.product.stock += obj.quantity # order 테이블은 product 테이블을 참조하고 있으므로 참조하고 있는 해당 product의 stock을 order의 quantity 만큼 다시 증가시킴
qs.update(status='환불') # qs 에 있는 모든 행들의 status 속성을 '환불'로 업데이트
actions = [ refund ] # 액션에 사용자 정의 액션인 refund 추가
3. Q
- queryset 조회문에서 filter (where문)를 적용할 때 논리적 연산이 수행될 수 있도록 객체를 캡슐화
- '~' NOT / ' ' AND / '|' OR
- from django.db.models import Q
4. LogEntry
- Admin 페이지에서 수행하는 모든 활동들을 기록하는 Log
- from django.contrib.admin.models import LogEntry, CHANGE
- LogEntry.objects.log_action() 은 액션 log 를 직접 생성하는 함수
LogEntry.objects.log_action
(
user_id = request.user.id, # 해당 액션을 수행하는 admin user의 id
content_type_id = ct.pk, # 해당 액션의 모델이 어떤 모델이고 어떤 타입인지 (log에 출력되는 모델명)
object_id = obj.pk, # 모델의 어떤 행인지 행의 id
object_repr = '주문 환불', # 액션 log에 출력될 내용
action_flag = CHANGE, # 액션 log에 출력되는 아이콘
change_message = '주문 환불'
)
5. 커스텀 액션 수행 결과
error
1. 같은 상품의 주문 환불 시 재고가 제대로 변경되지 않는 error
- 아래와 같이 같은 상품 (test_product4)의 주문을 환불하면 상태는 '환불'로 제대로 변경되지만 상품의 재고는 둘 다 적용되지 않고 하나만 적용되는 error
- 원인
- test_product4의 재고가 100개라면 queryset으로 2개의 주문 항목을 불러올 때 둘 다 obj.product.stock의 수가 100개이기 때문에 트랜잭션 충돌이 일어나게 됨 (갱신 분실)
즉, 첫 번째 항목을 환불할 때 stock의 값을 변경했더라도 두 번째 항목을 환불할 때 또 다시 100개를 기준으로 작업을하고 해당 결과를 save()하기 때문에 첫 번째 stock 변경 값은 분실(lost)됨
- 해결 방법
- 현재 test_product4의 stock 값과 환불하려는 주문 항목의 test_product의 stock 값이 일치하면 그대로 작업을 수행하고 일치하지 않다면 queryset으로 불러온 이후에 값이 변경됬다는 의미이므로 변경된 값을 기준으로 작업을 수행