TransactionManagementError 隐藏根异常

luk*_*arm 6 django

当代码在 transaction.atomic 块中执行并引发异常时,数据库处理程序被标记为需要回滚。如果仍然在该 transaction.atomic 块中执行后续查询,则会引发以下错误:

TransactionManagementError:当前事务中发生错误。在“原子”结束之前您无法执行查询

在这一点上,实际的根错误被掩盖并且很难访问,让您需要跳入 Django 的事务代码。

一个可能导致此错误的简单示例:

def someview(request):
    with transaction.atomic():
       // do some things
       instance = SomeModel.objects.create(...)
       // some other db queries

@receiver(post_save, sender=SomeModel)
def non_critical_side_effect(
    sender, instance, created, raw, using, update_fields, **kwargs
):
    try:
        // some query that causes a database error
        SomeModelLog.objects.create(some_non_none_field=None)
    except IntegrityError:
        //notify ourselves, go on
        pass
Run Code Online (Sandbox Code Playgroud)

当您遇到这种情况时,您如何弄清楚真正发生了什么,以及您如何定期避免这种情况?

(下面的自我回答 - 但真正对别人的想法感兴趣!)

luk*_*arm 3

如果您正在使用django.db.transaction.atomic上下文管理器并且被 TransactionManagementError 困扰,您可以通过检查exc_valuewhenneeds_rollback设置为Truein的值来确定根本原因django.db.transaction.Atomic.__exit__。这应该是提示需要回滚事务的异常。

为了从一开始就避免这个错误,我采用了两种方法:

  • 避免将大块代码包装在transaction.atomic
  • 如果您确实需要更大的transaction.atomic块,请确保该块中任何可能失败而无需回滚整个事务的部分都包装在它们自己的子事务中。

我的原始示例已更正,以便尽管信号处理程序遇到数据库错误,视图仍可以继续执行:

def someview(request):
    with transaction.atomic():
       // do some things
       SomeModel.objects.create(invalid_field=123)

@receiver(post_save, sender=SomeModel)
def non_critical_side_effect(
    sender, instance, created, raw, using, update_fields, **kwargs
):
    try:
        with transaction.atomic():
            // some query that causes a database error
    except IntegrityError:
        // notify ourselves, go on
        pass
Run Code Online (Sandbox Code Playgroud)