使用django select_for_update而不回滚错误

时间:2017-08-13 09:35:01

标签: django transactions database-locking

我尝试使用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管理交易来推出自己的解决方案,但我认为有更简单的方法可以获得此功能。是否有内置的方式来实现我想要的?

1 个答案:

答案 0 :(得分:1)

我最终得到的东西看起来像这样:

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块中,反之亦然。