Django 1.6 - "The outermost 'atomic' block cannot use savepoint = False when autocommit is off."

Django 1.6 - "The outermost 'atomic' block cannot use savepoint = False when autocommit is off."

在 django 1.5 天,如果我想手动管理事务(或事务中的事务),我会这样做:

@transaction.commit_manually
def my_method():
    master_sid = transaction.savepoint()
    for item in things_to_process:
        inner_sid = transaction.savepoint()
        # Make changes, save models, etc.
        ...
        if I_want_to_keep_this_iterations_changes:
            transaction.savepoint_commit(inner_sid)
        else:
            transaction.savepoint_rollback(inner_sid)
    if I_want_to_keep_all_un_rolled_back_changes_from_loop:
        transaction.savepoint_commit(master_sid)
    else:
        transaction.savepoint_rollback(master_sid)

如果我对 the Django docs 的理解正确,当升级到 Django 1.6+ 时,我应该将上面的内容更改为如下内容:

def my_method():
    transaction.set_autocommit(False)
    try:
        # Same code as above
    finally:
        transaction.set_autocommit(True)

但是,在 Django 1.6+ 中,如果您在自动提交为 False 时调用 model.save(),Django 将引发以下错误:

TransactionManagementError: The outermost 'atomic' block cannot use savepoint = False when autocommit is off.

那么,当自动提交为 false 时如何保存模型? 我的旧 Django 1.5 代码的现代替代品是什么?

你应该阅读 controlling transactions explicitly

特别是

If you attempt to run database queries before the rollback happens, Django will raise a TransactionManagementError. You may also encounter this behavior when an ORM-related signal handler raises an exception.

You may use atomic when autocommit is turned off. It will only use savepoints, even for the outermost block, and it will raise an exception if the outermost block is declared with savepoint=False.

您应该使用 transaction.atomic() 代替:

def my_method():
    with transaction.atomic():
        for item in things_to_process:
            with transaction.atomic():
                # Make changes, save models, etc.
                ...
                if I_want_to_roll_back_this_iterations_changes:
                    raise Exception('inner rollback')
        if I_want_to_roll_back_changes_from_loop:
            raise Exception('mater rollback')

transaction.atomic()会处理commit和rollback,可以嵌套。

Relevant docs