可以在 Django transaction.atomic() 中捕获并重新引发异常吗?

Sco*_*ord 7 python django transactions django-orm

Django 的文档这样说transaction.atomic()和例外:

https://docs.djangoproject.com/en/1.10/topics/db/transactions/#django.db.transaction.atomic

避免在 atomic 中捕获异常!

退出原子块时,Django 会查看它是正常退出还是异常退出,以确定是提交还是回滚。如果您在原子块内捕获并处理异常,您可能会向 Django 隐藏发生了问题的事实。这可能会导致意外行为。

这主要是 DatabaseError 及其子类(如 IntegrityError)的一个问题。出现这样的错误后,事务被破坏,Django 将在原子块的末尾执行回滚。如果您尝试在回滚发生之前运行数据库查询,Django 将引发 TransactionManagementError。当与 ORM 相关的信号处理程序引发异常时,您也可能会遇到此行为。

捕获数据库错误的正确方法是围绕原子块,如上所示。如有必要,为此添加一个额外的原子块。这种模式还有另一个优点:它明确地界定了发生异常时哪些操作将被回滚。

如果您捕获由原始 SQL 查询引发的异常,Django 的行为是未指定的并且依赖于数据库。

这样做是否可以,或者这是否会导致“意外行为”?

with transaction.atomic():
    # something
    try:
        # something
    except:
        logger.exception("Report error here.")
        raise
    
Run Code Online (Sandbox Code Playgroud)

dah*_*ens 7

根据文档,我将确保重新引发正确的异常,您可以独立处理其他错误。对于 django 来说,只需要在与数据库对话时收到有关出错的通知。

with transaction.atomic():
    # something
    try:
        # something
    except DatabaseError as db_err:
        logger.exception("Report error here.")
        raise db_err
    except Exception:
        # do something else
        # no need to reraise
        # as long as you handle it properly and db integrity is guaranteed
Run Code Online (Sandbox Code Playgroud)

  • 但随后他们会发出模糊的威胁,例如“这主要是针对 DatabaseError 及其子类的问题......”。大多?我主要做什么? (3认同)

Shi*_*agi 6

这个例子将消除你的疑虑。

with transaction.atomic():
    try:
        # if you do something that raises ONLY db error. ie. Integrity error
    except Exception:
        # and you catch that integrity error or any DB error like this here.
        # now if you try to update your DB
        model = Model.objects.get(id=1)
        model.description = "new description"
        model.save()
        # This will be illegal to do and Django in this case 
        # will raise TransactionManagementError suggesting 
        # you cannot execute any queries until the end of atomic block.
Run Code Online (Sandbox Code Playgroud)

现在,如果您像这样引发自定义异常:

with transaction.atomic():
    try:
        # raising custom exception
        raise Exception("some custom exception")
    except Exception:
        # and you catch that exception here
        # now if you try to update your DB
        model = Model.objects.get(id=1)
        model.description = "new description"
        model.save()
        # Django will allow you to update the DB.
Run Code Online (Sandbox Code Playgroud)