如何在循环django中回滚事务

时间:2018-02-27 15:39:32

标签: python django database transactions

如果循环中抛出异常,我试图回滚一组事务。但我不想突破循环或抛出异常而不抓住它。

如果循环中的任何子节点抛出异常,我不希望保存业务逻辑。这意味着我无法将事务放在循环中,因为如果它们中的任何一个失败,它只会回滚特定子项的事务。

parent = Parent.objects.get(pk='something')
exceptions = []
with transaction.atomic():
    for child in parent.children.all():
        try:
            # business logic which also saves other models
            # I don't want this saved if there is an exception for any object in the loop
        except Exception as e:
            exceptions.append({
                'id': child.id,
                'error': str(e),
            })
if len(exceptions) > 0:
    transaction.set_rollback(True)
    for exception in exceptions:
        Child.objects.filter(pk=exception['id']) \
            .update(error=exception['error']
    # more business logic and raise exception
    parent.is_blocked = True
    parent.save()
    # I don't want this exception to rollback all transactions
    raise Exception('Parent {} is blocked'.format(parent.id))

我收到上述代码的错误。这条消息很简单。我试图在块之外回滚事务。

django.db.transaction.TransactionManagementError: The rollback flag doesn't work outside of an 'atomic' block.

有没有人找到办法处理这样的事情。我希望我只是遗漏了一些简单的东西。如果您需要更多信息,请与我们联系。

2 个答案:

答案 0 :(得分:1)

Avoid catching exceptions inside atomic!

按照文档进行操作,在特殊情况下,您的代码应如下所示:

parent = Parent.objects.get(pk='something')
exceptions = []
try:
    with transaction.atomic():
        for child in parent.children.all():
            try:
                # business logic which also saves other models
                # I don't want this saved if there is an exception for any object in the loop
            except Exception as e:
                exceptions.append({
                    'id': child.id,
                    'error': str(e),
                })
        # raise exception handly to trigger rollback
        if len(exceptions) > 0:
            raise("raise for rollback")
except Exception as e:
    pass

if len(exceptions) > 0:
    for exception in exceptions:
        Child.objects.filter(pk=exception['id']) \
            .update(error=exception['error']
    # more business logic and raise exception
    parent.is_blocked = True
    parent.save()
    # I don't want this exception to rollback all transactions
    raise Exception('Parent {} is blocked'.format(parent.id))

答案 1 :(得分:0)

您可以尝试生成器功能:

def function():
    for child in parent.children.all():
        try:
            yield result
        except Exception as e:
            yield exception

为清楚起见,您可以查看此答案: How to handle error thrown in a generator function