在 Django 中可以通過django.db.transaction 模塊提供的atomic來定義一個事務
atomic提供兩種方案實現事務
- 裝飾器用法:
from django.db import transaction
@transaction.atomic
def viewfunc(request):
# 這些代碼會在一個事務中執行
......
裝飾器用法:整個視圖中所有 MySQL 數據庫的操作都看做一個事務,范圍太大,不夠靈活。而且無法直接作用於類視圖
- with 語句用法:
from django.db import transaction
def viewfunc(request):
# 這部分代碼不在事務中,會被 Django 自動提交
......
with transaction.atomic():
# 這部分代碼會在事務中執行
......
with 語句用法:可以靈活的有選擇性的把某些 MySQL 數據庫的操作看做一個事務。而且不用關心視圖的類型。
- 三句話使用
from django.db import transaction
# 創建保存點
save_id = transaction.savepoint()
# 回滾到保存點
transaction.savepoint_rollback(save_id)
# 提交從保存點到當前狀態的所有數據庫事務操作
transaction.savepoint_commit(save_id)
使用事務保存訂單數據
class OrderCommitView(LoginRequiredJSONMixin, View):
"""訂單提交"""
def post(self, request):
"""保存訂單信息和訂單商品信息"""
# 獲取當前保存訂單時需要的信息
......
# 顯式的開啟一個事務
with transaction.atomic():
# 創建事務保存點
save_id = transaction.savepoint()
# 暴力回滾
try:
# 保存訂單基本信息 OrderInfo(一)
order = OrderInfo.objects.create(
order_id=order_id,
user=user,
address=address,
total_count=0,
total_amount=Decimal('0'),
freight=Decimal('10.00'),
pay_method=pay_method,
status=OrderInfo.ORDER_STATUS_ENUM['UNPAID'] if pay_method == OrderInfo.PAY_METHODS_ENUM['ALIPAY'] else
OrderInfo.ORDER_STATUS_ENUM['UNSEND']
)
# 從redis讀取購物車中被勾選的商品信息
redis_conn = get_redis_connection('carts')
redis_cart = redis_conn.hgetall('carts_%s' % user.id)
selected = redis_conn.smembers('selected_%s' % user.id)
carts = {}
for sku_id in selected:
carts[int(sku_id)] = int(redis_cart[sku_id])
sku_ids = carts.keys()
# 遍歷購物車中被勾選的商品信息
for sku_id in sku_ids:
# 查詢SKU信息
sku = SKU.objects.get(id=sku_id)
# 判斷SKU庫存
sku_count = carts[sku.id]
if sku_count > sku.stock:
# 出錯就回滾
transaction.savepoint_rollback(save_id)
return http.JsonResponse({
'code': RETCODE.STOCKERR,
'errmsg': '庫存不足'})
# SKU減少庫存,增加銷量
sku.stock -= sku_count
sku.sales += sku_count
sku.save()
# 修改SPU銷量
sku.goods.sales += sku_count
sku.goods.save()
# 保存訂單商品信息 OrderGoods(多)
OrderGoods.objects.create(
order=order,
sku=sku,
count=sku_count,
price=sku.price,
)
# 保存商品訂單中總價和總數量
order.total_count += sku_count
order.total_amount += (sku_count * sku.price)
# 添加郵費和保存訂單信息
order.total_amount += order.freight
order.save()
except Exception as e:
logger.error(e)
transaction.savepoint_rollback(save_id)
return http.JsonResponse({
'code': RETCODE.DBERR,
'errmsg': '下單失敗'})
# 提交訂單成功,顯式的提交一次事務
transaction.savepoint_commit(save_id)
# 清除購物車中已結算的商品
pl = redis_conn.pipeline()
pl.hdel('carts_%s' % user.id, *selected)
pl.srem('selected_%s' % user.id, *selected)
pl.execute()
# 響應提交訂單結果
return http.JsonResponse({'code': RETCODE.OK,
'errmsg': '下單成功',
'order_id': order.order_id})