Django 的 on_commit 的用例是什么?

问题描述 投票:0回答:2

阅读本文档https://docs.djangoproject.com/en/4.0/topics/db/transactions/#django.db.transaction.on_commit

这是

on_commit

的用例
with transaction.atomic():  # Outer atomic, start a new transaction
    transaction.on_commit(foo)
    # Do things...

    with transaction.atomic():  # Inner atomic block, create a savepoint
        transaction.on_commit(bar)
        # Do more things...

# foo() and then bar() will be called when leaving the outermost block

但是为什么不像平常一样编写没有

on_commit
钩子的代码呢?像这样:

with transaction.atomic():  # Outer atomic, start a new transaction
    # Do things...

    with transaction.atomic():  # Inner atomic block, create a savepoint
        # Do more things...

foo()
bar()

# foo() and then bar() will be called when leaving the outermost block

它更容易阅读,因为它不需要更多关于 Django API 的知识,并且语句按照执行的顺序排列。测试更容易,因为您不必为 Django 使用任何特殊的测试类。

那么

on_commit
钩子的用例是什么?

python django transactions commit
2个回答
25
投票

Django 文档中给出的示例代码是

transaction.on_commit(lambda: some_celery_task.delay('arg1'))
,它可能是专门为 celery 任务而出现的。

想象一下,如果您在交易中执行以下操作:

my_object = MyObject.objects.create()
some_celery_task.delay(my_object.pk)

然后在你的芹菜任务中你尝试这样做:

@app.task
def some_celery_task(object_pk)
    my_object = MyObject.objects.get(pk=object_pk)

这可能在很多时候都有效,但随机地你会遇到无法找到对象的错误(取决于工作任务运行的速度,因为它是竞争条件)。 这是因为您在事务中创建了

MyObject
记录,但在运行
COMMIT
之前,它实际上在数据库中不可用。 Celery 无法访问该打开的事务,因此需要在 COMMIT 之后运行它。 还有一种非常现实的可能性是,稍后的某些事情会导致
ROLLBACK
并且 celery 任务实际上不应该被调用。
所以...你需要做:

my_object = MyObject.objects.create() transaction.on_commit(lambda: some_celery_task.delay(my_object.pk))

现在,在调用 
MyObject

后,直到

COMMIT
实际上 已保存到数据库后,才会调用 celery 任务。
不过,我应该注意,这主要只是当您不使用 

AUTOCOMMIT

(实际上是默认设置)时才需要担心。 如果您处于

AUTOCOMMIT
模式,那么您可以确定提交已作为
.create()
.save()
的一部分完成。 但是,如果您的代码库有可能在
@transaction.atomic()
中被调用,那么它就不再是
AUTOCOMMIT
并且您又需要
.on_commit()
,所以最好/最安全的是始终使用它。
    


9
投票

Django 提供了 on_commit() 函数来注册事务
成功提交后应执行的回调函数

这是主要目的。事务是您希望以原子方式处理的工作单元。它要么完全发生,要么根本不发生。这同样适用于您的代码。如果数据库操作期间出现问题,您可能不需要执行某些操作。

让我们考虑一些业务逻辑流程:

用户将其注册数据发送到我们的端点,我们对其进行验证等。
  1. 我们将新用户保存到我们的数据库中。
  2. 我们通过电子邮件向他们发送一封“你好”信,其中包含用于确认他的帐户的链接。
  3. 如果第 2 步出现问题,我们不应该进入第 3 步。

我们可以认为,我会得到一个异常,并且也不会执行该代码。为什么我们还需要它?

有时,您在代码中采取的操作是基于在潜在危险的数据库操作之前事务成功的假设。例如,您首先要检查是否可以向您的用户发送电子邮件,因为您知道您的电子邮件第三方经常给您 500。在这种情况下,您想为用户筹集 500 并要求他们稍后注册(顺便说一句,这是一个非常糟糕的主意,但这只是一个综合示例)。

当您的函数(例如使用

@atomic

装饰器)包含大量数据库操作时,您肯定不希望记住所有变量状态以便在所有与数据库相关的代码之后使用它们。像这样:


验证用户订单。
  • 检查DB是否可以完成。
  • 如果可以的话,我们需要向第 3 方 CRM 发送包含订单详细信息的请求。
  • 如果不能,那么我们应该在另一个第 3 方创建支持票证。
  • 将用户订单保存到数据库,更新用户模型。
  • 向负责订单的员工发送消息通知。
  • 保存信息,员工通知已成功发送至数据库。
  • 你可以想象,如果我们在这种情况下没有
on_commit

,我们会陷入什么样的混乱,而我们对此有很大的

try-catch
    

© www.soinside.com 2019 - 2024. All rights reserved.