在Django中成功使用嵌套@transaction.commit_
考虑这个简单的例子:在Django中成功使用嵌套@transaction.commit_,django,transactions,Django,Transactions,考虑这个简单的例子: # a bank account class class Account: @transaction.commit_on_success def withdraw(self, amount): # code to withdraw money from the account @transaction.commit_on_success def add(self, amount): # code to ad
# a bank account class
class Account:
@transaction.commit_on_success
def withdraw(self, amount):
# code to withdraw money from the account
@transaction.commit_on_success
def add(self, amount):
# code to add money to the account
# somewhere else
@transaction.commit_on_success
def makeMoneyTransaction(src_account, dst_account, amount):
src_account.withdraw(amount)
dst_account.add(amount)
(摘自)
如果在Account.add()
中出现异常,则Account.draw()
中的交易仍将提交,资金将丢失,因为Django当前不处理嵌套交易
如果不对Django应用修补程序,我们如何确保提交被发送到数据库,但仅当@transaction.commit\u on\u success
decorator下的主函数完成而不引发异常时
我偶然发现了这个片段:它似乎可以完成这项工作。如果我使用它,有什么缺点需要注意吗
如果你能帮忙的话,请提前表示衷心的感谢
另外,为了便于查看,我复制了前面引用的代码片段:
def nested_commit_on_success(func):
"""Like commit_on_success, but doesn't commit existing transactions.
This decorator is used to run a function within the scope of a
database transaction, committing the transaction on success and
rolling it back if an exception occurs.
Unlike the standard transaction.commit_on_success decorator, this
version first checks whether a transaction is already active. If so
then it doesn't perform any commits or rollbacks, leaving that up to
whoever is managing the active transaction.
"""
commit_on_success = transaction.commit_on_success(func)
def _nested_commit_on_success(*args, **kwds):
if transaction.is_managed():
return func(*args,**kwds)
else:
return commit_on_success(*args,**kwds)
return transaction.wraps(func)(_nested_commit_on_success)
这个代码段的问题在于,它不能在不回滚外部事务的情况下回滚内部事务。例如:
@nested_commit_on_success
def inner():
# [do stuff in the DB]
@nested_commit_on_success
def outer():
# [do stuff in the DB]
try:
inner()
except:
# this did not work, but we want to handle the error and
# do something else instead:
# [do stuff in the DB]
outer()
在上面的示例中,即使internal()
引发异常,其内容也不会回滚
你需要的是一个内部“事务”的框架。对于您的代码,它可能如下所示:
# a bank account class
class Account:
def withdraw(self, amount):
sid = transaction.savepoint()
try:
# code to withdraw money from the account
except:
transaction.savepoint_rollback(sid)
raise
def add(self, amount):
sid = transaction.savepoint()
try:
# code to add money to the account
except:
transaction.savepoint_rollback(sid)
raise
# somewhere else
@transaction.commit_on_success
def makeMoneyTransaction(src_account, dst_account, amount):
src_account.withdraw(amount)
dst_account.add(amount)
从Django 1.6开始,decorator就完全做到了这一点:它使用一个事务作为decorator的外部使用,而任何内部使用都使用一个保存点。Django 1.6介绍了这一点,这正是我想要的
它不仅支持“嵌套”事务,而且还取代了旧的、功能较弱的装饰程序。在不同的Django应用程序之间有一个独特且一致的事务管理行为是很好的。我甚至不确定这个代码片段是否能正常工作。现在它似乎已经4岁了,有人说它在这里不起作用:谢谢,你的解决方案看起来确实更安全。我希望Django将在未来的版本中引入使用保存点的@atomic decorator。同时,我会继续使用我的,因为它使用起来更简单。如果不忽略从子方法引发的异常,它仍然会工作。