【问题标题】:django: commit and raise inside transaction.atomic()django:在 transaction.atomic() 中提交和引发
【发布时间】:2017-04-07 13:37:37
【问题描述】:

我们正在尝试从 commit_manually 迁移到 atomic,以便我们可以在旧项目中将 Django 升级到至少 1.8。在大多数情况下,我们需要这样做:

with transaction.atomic():
    obj = Entity.objects.select_for_update().get(pk=pk)
    try:
        obj.do_something()
        obj.set_some_status()
        obj.save()
    except SomeException:
        obj.set_failed_flag()
        obj.save()
        raise

因为被调用者需要这个异常信息来继续特定的流程。但在这种情况下,事务/保存点将被回滚,这不是我们想要的,因为我们希望提交 obj.set_failed_flag()。此外,将它设置在同一个原子块内似乎是合乎逻辑的,因为我们已经为这个对象锁定了一行。

有什么想法/模式吗?提前致谢!

附:使用旧的手动事务管理非常简单!

附言我们也为“提前退出”使用异常,并且移动到一些标志等会带来混乱的日志,我个人很想避免它。

【问题讨论】:

    标签: python django transactions django-orm


    【解决方案1】:

    假设SomeException 不是数据库异常,您可以将其保存并在原子块之外引发:

    with transaction.atomic():
        obj = Entity.objects.select_for_update().get(pk=pk)
    
        try:
            obj.do_something()
            obj.set_some_status()
        except SomeException as e:
            obj.set_failed_flag()
            exception = e
        else:
            exception = None
    
        obj.save()
    
    if exception:
        raise exception
    

    如果您觉得这太冗长并且需要经常这样做,您可以编写一个上下文管理器来充当transaction.atomic() 的代理,但在某些情况下不会触发回滚。

    最后,请注意 Django 仍然有manual transaction management

    【讨论】:

    • 感谢凯文,这种方法正是我正在考虑并实际上想要避免的。我在考虑手动事务管理,但我们也使用保存点。在这种情况下 atomic() 是一件很棒的事情。
    【解决方案2】:

    除了已经发布的答案之外,如果有嵌套方法在链中更高的位置开始原子块,则使用以下方法会有所帮助:

    transaction.on_commit(lambda: method_that_raises_exception())
    

    这样在事务提交后会引发异常。

    【讨论】:

    • 有没有办法用transaction.atomic处理分布式事务?
    • @shadow0359 恐怕我不完全理解这个问题。你能详细说明一下吗?
    • 我想将事务用于两个独立的数据库连接。
    猜你喜欢
    • 2018-06-12
    • 2017-07-24
    • 1970-01-01
    • 2017-01-12
    • 1970-01-01
    • 2018-09-29
    • 1970-01-01
    • 2015-01-12
    • 1970-01-01
    相关资源
    最近更新 更多