有时你需要执行与当前数据库事务相关的操作,但前提是事务成功提交。
Django 提供了 on_commit() 函数来注册在事务成功提交后应该执行的回调函数:
将任意函数(无参数)传递给 on_commit()
:
from django.db import transaction
def do_something():
pass # send a mail, invalidate a cache, fire off a Celery task, etc.
transaction.on_commit(do_something)
你也可以使用 lambda
包装函数:
transaction.on_commit(lambda: some_celery_task.delay("arg1"))
传入的函数将在成功提交调用“on_commit()
”的假设数据库写操作后立即被调用。
无任何活动事务时调用 on_commit()
,则回调函数会立即执行。
如果假设的数据库写入被回滚(尤其是在 atomic()
块里引发了一个未处理异常),函数将被丢弃且永远不会被调用。
正确处理保存点(即嵌套了 atomic()
块)。也就是说,注册在保存点后的 on_commit()
的调用(嵌套在 atomic()
块)将在外部事务被提交之后调用,但如果在事务期间回滚到保存点或任何之前的保存点之前,则不会调用:
with transaction.atomic(): # Outer atomic, start a new transaction
transaction.on_commit(foo)
with transaction.atomic(): # Inner atomic block, create a savepoint
transaction.on_commit(bar)
# foo() and then bar() will be called when leaving the outermost block
另一方面,当保存点回滚时(因引发异常),内部调用不会被调用:
with transaction.atomic(): # Outer atomic, start a new transaction
transaction.on_commit(foo)
try:
with transaction.atomic(): # Inner atomic block, create a savepoint
transaction.on_commit(bar)
raise SomeError() # Raising an exception - abort the savepoint
except SomeError:
pass
# foo() will be called, but not bar()
事务提交后的的回调函数执行顺序与当初注册时的顺序一致。
如果一个带有给定事务的 on-commit
函数引发了未捕获的异常,那么同一个事务里的后续注册函数不会被运行。这与你在没有 on_commit()
的情况下顺序执行函数的行为是一样的。
你的回调会在成功提交之后执行,因此回调里的错误引发事务回滚。它们在事务成功时有条件的执行,但它们不是事务的一部分。对于有预期的用例(邮件提醒,Celery 任务等),这样应该没啥问题。如果它不是这样的用例(如果你的后续操作很关键,以至于它的错误意味着事务失败),那么你可能不需要使用 on_commit()
钩子。相反,你可能需要两阶段提交——比如两阶段提交协议支持( psycopg Two-Phase Commit protocol support )和在 Python DB-API 里说明的可选两阶段提交扩展( optional Two-Phase Commit Extensions in the Python DB-API specification ) 。
直到在提交后的连接上恢复自动提交,调用才会运行。(因为否则在回调中完成的任何查询都会打开一个隐式事务,防止连接返回自动提交模式)
当在自动提交模式并且在 atomic()
块外时,函数会立即自动运行,而不会提交。
on-commit
函数仅适用于自动提交模式( autocommit mode
),并且 atomic()
(或 ATOMIC_REQUESTS
)事务API。当禁用自动提交并且当前不在原子块中时,调用 on_commit()
将导致错误。
Django 的 TestCase
类将每个测试包装在一个事务中,并在每次测试后回滚该事务,以提供测试隔离。 这意味着实际上没有任何事务被提交,因此您的 on_commit()
回调将永远不会运行。
您可以通过使用 TestCase.captureOnCommitCallbacks()
来克服这个限制。 这会在列表中捕获您的 on_commit()
回调,允许您对它们进行断言,或通过调用它们来模拟事务提交。
克服限制的另一种方法是使用 TransactionTestCase
而不是 TestCase
。 这意味着您的事务已提交,并且回调将运行。 但是 TransactionTestCase
在测试之间刷新数据库,这比 TestCase
的隔离要慢得多。
事务回滚钩子相比事务提交钩子更难实现,因为各种各样的情况都可能造成隐式回滚。
比如,如果数据库连接被删除,因为进程被杀而没有机会正常关闭,回滚钩子将不会运行。
解决方法是:与其在执行事务时(原子操作)进行某项操作,当事务执行失败后再取消这项操作,不如使用 on_commit()
来延迟该项操作,直到事务成功后再进行操作。毕竟事务成功后你才能确保之后的操作是有意义的。
ASP.NET Calendar CellPadding 属性 Calendar 控件定义和用法 CellPadding 属性用于设置日历单元格边框与其内容之间的空白。 语...
ASP.NET ImageButton ValidationGroup 属性 ImageButton 控件定义和用法 ValidationGroup 属性规定了在验证时被验证的控件组。 ...
ASP.NET AdRotator 控件 Web 服务器控件定义和用法 AdRotator 控件用于显示图像序列。 该控件使用 XML 文件来存储 ad 信息。XML ...
ASP.NET Button CausesValidation 属性 Button 控件定义和用法 CausesValidation 属性规定当 Button 控件被点击时是否验证页面。...
WebSecurity -GetCreateDate()WebSecurity 对象定义和用法 GetCreateDate() 方法返回指定会员帐户创建的日期和时间。 C# 和 VB ...
ASP.NET Rows 属性 ListBox 控件定义和用法 属性用于获取或设置 ListBox 中的可见行数(无滚动)。 语法asp:ListBox Rows="num" ...
ASP.NET HtmlImage 控件 HTML 服务器控件定义和用法 HtmlImage 控件用于控制 img 元素。在 HTML 中,img 元素用于显示图像。属性...
ASP.NET BulletedList 控件 Web 服务器控件定义和用法 BulletedList 能够以项目符号的格式来创建列表。 BulletedList 控件中的每...
ASP.NET BorderStyle 属性 Web 控件标准属性参考手册定义和用法 BorderStyle 属性用于设置或返回控件的边框样式。 语法asp:webco...