使用 django select_for_update 不回滚错误

Using django select_for_update without rolling back on error

我正在尝试通过 select_for_update 实用程序来利用 django 的行级锁定。根据文档,这只能在 transaction.atomic 块内使用。使用 transaction.atomic 块的副作用是,如果我的代码抛出异常,所有数据库更改都会回滚。我的用例是这样的,我实际上想保留数据库更改,并允许异常传播。这让我的代码看起来像这样:

with transaction.atomic():
    user = User.objects.select_for_update.get(id=1234)
    try:
        user.do_something()
    except Exception as e:
        exception = e
    else:
        exception = None

if exception is not None:
    raise exception

这感觉完全是一种反模式,我确定我一定遗漏了什么。我知道我可以通过手动使用 transaction.set_autocommit 来管理事务来推出我自己的解决方案,但我认为会有更简单的方法来获得此功能。有没有内置的方法来实现我想要的?

我最终选择了如下所示的内容:

from django.db import transaction

class ErrorTolerantTransaction(transaction.Atomic):

    def __exit__(self, exc_type, exc_value, traceback):
        return super().__exit__(None, None, None)


def error_tolerant_transaction(using=None, savepoint=True):
    """
    Wraps a code block in an 'error tolerant' transaction block to allow the use of
    select_for_update but without the effect of automatic rollback on exception.

    Can be invoked as either a decorator or context manager.
    """
    if callable(using):
        return ErrorTolerantTransaction('default', savepoint)(using)

    return ErrorTolerantTransaction(using, savepoint)

我现在可以用 error_tolerant_transaction 代替 transaction.atomic 并且可以在不强制回滚的情况下引发异常。当然,与数据库相关的异常(即 IntegrityError)仍会导致回滚,但鉴于我们正在使用事务,这是预期的行为。作为奖励,此解决方案与 transaction.atomic 兼容,这意味着它可以嵌套在 atomic 块中,反之亦然。