from django.shortcuts import render
from django.http import HttpResponse
from django.views.generic import View
from django.db import transaction # 导入事务
# 类视图 (事务,@transaction.atomic装饰器)
class MyView(View):
@transaction.atomic
# transaction.atomic装饰器可以保证该函数中所有的数据库操作都在一个事务中。
def post(self, request):
# 数据库操作1。。。
# 数据库操作2。。。
return HttpResponse('ok')
# 类视图 (事务,保存点的使用)
class MyView2(View):
@transaction.atomic
def post(self, request):
# 设置事务保存点
s1 = transaction.savepoint() # 可以设置多个保存点
# 数据库操作。。。
# 事务回滚 (如果发生异常,就回滚事务)
transaction.savepoint_rollback(s1) # 可以回滚到指定的保存点
# 提交事务 (如果没有异常,就提交事务)
transaction.savepoint_commit(s1)
# 返回应答
原文链接:https://blog.csdn.net/houyanhua1/article/details/85246618
本人案例:直接上代码了
from rest_framework import serializers from apps.goods.models import SKU, SKUSpecification from django.db import transaction from celery_tasks.detail.tasks import task_generate # 数据模型类:SKU具体规格-->SKUSpecification中定义了外键关联SKU数据模型类,从而在SKUSpecification表中获得spec_id,option_id两个值 class SkuSpecRelatedSerializer(serializers.Serializer): spec_id = serializers.IntegerField() option_id = serializers.IntegerField() class SkuSerializer(serializers.ModelSerializer): # 标准商品spu在SKU 数据模型类model中作为外键, 下面将标准商品以字符串输出 spu = serializers.StringRelatedField(read_only=True) # 标准商品作为外键,默认有一个隐藏属性spu_id, 尽管下面fields= "__all__",但是隐藏属性字段不会返回数据,如果想要数据需要明确定义 spu_id = serializers.IntegerField() # 指定三级分类category_id作为外键 输出名称 category = serializers.StringRelatedField(read_only=True) # 定义隐藏属性 category_id = serializers.IntegerField() # 规格信息调用上面的SkuSpecRelatedSerializer # specs = SkuSpecRelatedSerializer(read_only=True, many=True) # 创建Sku时需要规格数据,删除read_only=True参数 specs = SkuSpecRelatedSerializer(many=True) class Meta: model = SKU # fields = "__all__" exclude = ['create_time', 'update_time'] # 重写ModelSerializer —— create方法 def create(self, validated_data): # SKU对应的表中没有属性specs,所以需要从字典中删除 specs = validated_data.pop('specs') # 调用父类方法实现SKU对象创建 # instance = super().create(validated_data) with transaction.atomic(): # 禁止自动提交,保证该函数中的所有数据库操作在同一个事物中,第一个数据库操作1即使成功保存到数据库中,只要第2个数据操作失败,那么所有该段代码所有设计的都会更改回滚到原来 sid = transaction.savepoint() # 开启事务设置事务保存点 try: # 直接创建SKU对象 instance = SKU.objects.create(**validated_data) # 遍历,创建SKU规格对象 for item in specs: spec_id = item.get('spec_id') option_id = item.get('option_id') SKUSpecification.objects.create(sku_id=instance.id, spec_id=spec_id, option_id=option_id) except: transaction.savepoint_rollback(sid) # 失败回滚事务(如果数据库操作发生异常,回滚到设置的事务保存点) raise serializers.ValidationError("数据保存失败") else: transaction.savepoint_commit(sid) # 如果没有异常,成功提交事物 # 为Sku生成静态文件 task_generate.delay(instance.id) return instance
案例2:事务--支付:
#coding=utf-8 import sys import MySQLdb class TransferMoney(object): def __init__(self,conn): self.conn = conn #检查账户是否合法 def check_acct_avaiable(self,acctid): cursor = self.conn.cursor() try: sql = "select * from account where acctid=%s" % acctid cursor.execute(sql) print "check account:" + sql rs = cursor.fetchall() if len(rs) != 1: raise Exception("account %s illega" % acctid) finally: cursor.close() #检查是否有足够的钱 def has_enough_money(self,acctid,money): cursor = self.conn.cursor() try: sql = "select * from account where acctid=%s and money > %s" % (acctid,money) cursor.execute(sql) print "has enough money:" + sql rs = cursor.fetchall() if len(rs) != 1: raise Exception("account %s not enough money" % acctid) finally: cursor.close() #账户减钱 def reduce_money(self,acctid,money): cursor = self.conn.cursor() try: sql = "update account set money = money-%s where acctid = %s" % (money,acctid) cursor.execute(sql) print "reduce_money:" + sql if cursor.rowcount != 1: raise Exception("reduce money fail %s" % acctid) finally: cursor.close() #账户加钱 def add_money(self,acctid,money): cursor = self.conn.cursor() try: sql = "update account set money = money+%s where acctid = %s" % (money,acctid) cursor.execute(sql) print "add_money:" + sql if cursor.rowcount != 1: raise Exception("add money fail %s" % acctid) finally: cursor.close() #主执行语句 def transfer(self,source_acctid,target_acctid,money): try: self.check_acct_avaiable(source_acctid) self.check_acct_avaiable(target_acctid) self.has_enough_money(source_acctid,money) self.reduce_money(source_acctid,money) self.add_money(target_acctid,money) self.conn.commit() except Exception as e: self.conn.rollback() raise e if __name__ == "__main__": source_acctid = sys.argv[1] target_acctid = sys.argv[2] money = sys.argv[3] conn = MySQLdb.Connect(host = '127.0.0.1',port=3306,user='root',passwd='',db='test',charset='utf8') tr_money = TransferMoney(conn) try: tr_money.transfer(source_acctid,target_acctid,money) except Exception as e: print "Happen:" + str(e) finally: conn.close()
针对个人代码总结:
-
-
-
设置该函数中的所有数据库操作在同一个事物中,第一个数据库操作1即使成功保存到数据库中,只要第2个数据操作失败,那么所有该段代码所有涉及的数据库操作都会更改回滚到原来。
with transaction.atomic()
-
设置事务保存点并开启事务
sid = transaction.savepoint() # 开启事务设置事务保存点
-
数据库读写操作
操作1
操作2
-
失败回滚事务(条件:任意一个操作失败,数据库操作发生异常,回滚到设置的事务保存点)
transaction.savepoint_rollback(sid)
raise serializers.ValidationError("数据保存失败") -
成功(条件:操作1和2都成功,提交事务)
transaction.savepoint_commit(sid) # 如果没有异常,成功提交事务
-
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Python Django,事务,transaction.atomic,事务保存点 - Python技术站