ale*_*ine 6 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 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)
Run Code Online (Sandbox Code Playgroud)
(取自https://code.djangoproject.com/ticket/2227)
如果异常进入Account.add()
,Account.withdraw()
则仍会提交事务并且资金将丢失,因为Django当前不处理嵌套事务.
如果不向Django应用补丁,我们怎样才能确保将提交发送到数据库,但只有当@transaction.commit_on_success
装饰器下的main函数完成而没有引发异常时?
我遇到了这个片段:http://djangosnippets.org/snippets/1343/,看起来它可以完成这项工作.如果我使用它,我应该注意哪些缺点?
如果您能提供帮助,请提前感谢.
PS我正在复制先前引用的代码片段以实现可见性:
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)
Run Code Online (Sandbox Code Playgroud)
此代码段的问题在于,它不会使您能够在不回滚外部事务的情况下回滚内部事务.例如:
@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()
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,即使inner()
引发异常,也不会回滚其内容.
你需要的是内部"交易" 的保存点.对于您的代码,它可能如下所示:
# 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)
Run Code Online (Sandbox Code Playgroud)
从Django 1.6开始,atomic()装饰器就是这样做的:它使用一个事务来进行装饰器的外部使用,任何内部使用都使用一个保存点.
归档时间: |
|
查看次数: |
3352 次 |
最近记录: |