Django框架默认的事务行为
自动提交作为Django默认的事务行为,它表现形式为:每次数据库操作会立即被提交到数据库中,除非这个事务仍然处于激活状态。 那么,更多详细内容见下文。
Django使用事务或者保存点来保证多个ORM操作的完整性,尤其是针对delete()和update()操作。
另外因为某些性能原因,Django提供的TestCase类就将每个测试用例包裹在一个事务中。
给Http请求绑定事务
在Web应用中,常用的事务处理方式是将每个请求都包裹在一个事务中。这个功能使用起来非常简单,你只需要将它的配置项ATOMIC_REQUESTS设置为True。
它是这样工作的:当有请求过来时,Django会在调用视图方法前开启一个事务。如果请求却正确处理并正确返回了结果,Django就会提交该事务。否则,Django会回滚该事务。
同样,你可以在视图代码中使用保存点来担任子事务的角色,典型的例子是atomic()
上下文管理器。那么,最后所有更改要么被提交,要么被回滚。
Django官方推荐使用中件间TransactionMiddleware来处理请求和响应中的事务。它的工作原理是这样的:当一个请求到来时,Django开始一个事务,如果响应没有出错,Django提交这期间所有的事务,如果view中的函数抛出异常,那么Django会回滚这之间的事务。
为了实现这个特性,需要在MIDDLEWARE_CLASSES setting中添加TransactionMiddleware:
MIDDLEWARE_CLASSES = ( 'django.middleware.cache.UpdateCacheMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.transaction.TransactionMiddleware', 'django.middleware.cache.FetchFromCacheMiddleware', )
顺序很重要,TransactionMiddleware中间件会将置于其后的中间件都包含在事务的范围之中(用于缓存的中间件除外,他们不受影响,例如CacheMiddleware,UpdateCacheMiddleware和FetchFromCacheMiddleware)。
另外需要注意的是,TransactionMiddleware只会影响DATABASES设置中的默认的数据库,对于其它的数据库,如果我们实现事务控制的话只能用别的方案了。
警告!!!
虽然这种事务模式的优势在于它的简单性,但在访问量增长到一定的时候会造成很大的性能损耗。这是因为为每一个视图开启一个事物会有一些额外的开销。
另外,这种性能影响还取决于你的应用程序的查询模式以及你的数据库对锁的处理是否高效。
基于请求的事务和流式响应 当一个视图返回一个StreamingHttpResponse时,读取响应的内容通常会执行一段代码去生成内容。但由于视图已经返回了结果,这些代码将运行在事务之外。 一般而言,不建议在生成流式响应的时候写入数据库,因为目前还没有一个很好的方法来处理响应已经被发送之后的错误。
在实践中,可以简单使用atomic()
装饰器来装饰每一个视图方法来实现该功能。
需要注意的是,它有个前提:你的视图代码运行在封闭的事务中。例如,中间件就只能运行在事务之外,这么说来,就不难理解为什么响应模板的渲染是不受事务控制了。
即便ATOMIC_REQUESTS被开启了,你仍然能有办法让视图方法运行在事务之外。
non_atomic_requests(using=None)[source]
比如,你就可以上面这个该装饰来让视图方法不受事务控制。
from django.db import transaction @transaction.non_atomic_requests def my_view(request): do_stuff() @transaction.non_atomic_requests(using='other') def my_other_view(request): do_stuff_on_the_other_database()
在VIEW中实现事务的控制
autocommit()
使用autocommit装饰器可以将view函数中的事务还原成Django默认的自动提交模式,无视全局事务的设置。
示例:
from django.db import transaction
@transaction.autocommit
def viewfunc(request):
....
@transaction.autocommit(using="my_other_database")
def viewfunc2(request):
...
commit_on_success()
顾名思义,view函数成功则提交事务,否则回滚。用法同上。
commit_manually()
告诉Django我们将自己控制函数中的事务处理。并且要注意,如果在视图函数中改变了数据库的数据并且没有调用commit() 或rollback(),那么将抛出TransactionManagementError异常。
示例:
from django.db import transaction
@transaction.commit_manually
def viewfunc(request):
..
# You can commit/rollback however and whenever you want
transaction.commit()
...
# But you've got to remember to do it yourself!
try:
..
except:
transaction.rollback()
else:
transaction.commit()
@transaction.commit_manually(using="my_other_database")
def viewfunc2(request):
from django.db import transaction def create_model(self,request): with transaction.atomic(): order_obj=models.Order.objects.create(**self.order_dict) temp=[] for item in self.order_list: item['order']=order_obj obj=models.OrderDetail(**item) temp.append(obj) models.OrderDetail.objects.bulk_create(temp) now=datetime.datetime.now() for obj in self.coupon_list: obj['account_id']=request.auth.user_id obj['status']=0 models.CouponRecord.objects.filter(**obj).update(status=1,used_time=now)