Управляемый блок транзакции завершился с ожиданием COMMIT / ROLLBACK - PullRequest
30 голосов
/ 13 апреля 2011

У меня есть функция просмотра:

@transaction.commit_manually
def xyz(request):
    if ABC:
        success = something()

        if success:
            status = "success"
            transaction.commit()

        else:
            status = "dataerrors"
            transaction.rollback()
    else:
        status = "uploadproblem"
        transaction.rollback()

    return render(request, "template.html", {
        'status': status,
    })

Я считаю, что каждый путь кода так или иначе завершает транзакцию.Но Джанго, кажется, жалуется, что это не так.Любые идеи?

Django Version:     1.3
Exception Type:     TransactionManagementError
Exception Value:    Transaction managed block ended with pending COMMIT/ROLLBACK

РЕДАКТИРОВАТЬ: Никакие другие исключения не создаются для изменения пути кода.

Ответы [ 9 ]

85 голосов
/ 22 июля 2011

Получив подобную проблему и потратив на нее часы, я понял, как ее отладить.

По какой-то причине декоратор @action.commit_manually отключает исключения, возникающие в функции.

Временно удалите декоратор из вашей функции, теперь вы увидите исключение, исправите его и верните декоратор обратно!

9 голосов
/ 03 мая 2011

У меня была такая же проблема.Единственное решение, которое я нашел, - использовать предложение try / finally, чтобы гарантировать, что коммит произойдет после рендеринга.

@transaction.commit_manually
def xyz(request):
    committed = False
    try:
        if ABC:
            success = something()

            if success:
                status = "success"
                transaction.commit()
                committed = True

            else:
                status = "dataerrors"
                transaction.rollback()
                committed = True
        else:
            status = "uploadproblem"
            transaction.rollback()
            committed = True

        return render(request, "template.html", {
            'status': status,
        })
    finally:
        if not committed:
            transaction.rollback() # or .commit() depending on your error-handling logic

Не имеет смысла, но это сработало для меня.

2 голосов
/ 26 июня 2014

Другая причина, по которой вы можете столкнуться с этой проблемой, заключается в том, что в вашей системе несколько БД.

Я смог преодолеть эту ошибку с

@transaction.commit_manually(using='my_other_db')
def foo():
   try:
        <db query>
        transaction.commit(using='my_other_db')
   except:
        transaction.rollback(using='my_other_db')
2 голосов
/ 10 апреля 2014

У меня возникла та же проблема, и я узнал, что даже если вы правильно закроете транзакцию вручную в случае исключений, если вы затем снова запишете в orm в области действия ручной транзакции, она, похоже, как-то повторно открывает транзакцию и вызывает транзакцию исключение.

            with transaction.commit_manually():
                try:
                    <exciting stuff>
                    transaction.commit()                        
                except Exception, e:
                    transaction.rollback()
                    o.error='failed' <== caused transaction exception
1 голос
/ 04 мая 2015

Как говорили другие люди, исключения, возникающие в декорированной функции, "теряются", потому что они перезаписываются исключением TransactionManagementError.

Предлагаю расширить transaction.commit_manually декоратор. Мой декоратор transaction_commit_manually использует декоратор transaction.commit_manually внутри; если в оформленной функции возникает исключение, мой декоратор перехватывает исключение, выполняет transaction.rollback() и снова вызывает исключение. Таким образом, транзакция очищается правильно, и исходное исключение не теряется.

def _create_decorator_transaction_commit_manually(using=None):
    def deco(f):
        def g(*args, **kwargs):
            try:
                out = f(*args, **kwargs)
            except Exception as e:
                if using is not None:
                    transaction.rollback(using=using)
                else:
                    transaction.rollback()
                raise e
            return out
        if using is not None:
            return transaction.commit_manually(using=using)(g)
        return transaction.commit_manually(g)
    return deco

def transaction_commit_manually(*args, **kwargs):
    """
    Improved transaction.commit_manually that does not hide exceptions.

    If an exception occurs, rollback work and raise exception again
    """
    # If 'using' keyword is provided, return a decorator
    if 'using' in kwargs:
        return _create_decorator_transaction_commit_manually(using=kwargs['using'])
    # If 'using' keyword is not provided, act as a decorator:
    # first argument is function to be decorated; return modified function
    f = args[0]
    deco = _create_decorator_transaction_commit_manually()
    return deco(f)
1 голос
/ 06 февраля 2012

У меня была похожая проблема, может быть, этот код хорошо работает для вас:

@transaction.commit_on_success
def xyz(request):
    if ABC:
        success = something()

        if success:
            status = "success"

        else:
            status = "dataerrors"
            transaction.rollback()
    else:
        status = "uploadproblem"
        transaction.rollback()

    return render(request, "template.html", {
        'status': status,
    })
1 голос
/ 15 апреля 2011

Это всегда происходит, когда где-то в коде возникает необработанное исключение. В моем случае по какой-то причине исключение не было брошено в отладчик, что вызвало у меня замешательство.

0 голосов
/ 27 февраля 2015

Поместите свой код в блок try / исключением. В блоке кроме просто сделайте транзакцию .rollback и зарегистрируйте объект исключения.

@transaction.commit_manually
def xyz(xyz):
   try:
       some_logic
       transaction.commit()
    except Exception,e:
       transaction.rollback()
       print str(e)
0 голосов
/ 13 апреля 2011

У меня была та же проблема, и я пробовал разные подходы. Вот что сработало для меня, но я не уверен, что это правильный способ сделать это. Измените свое заявление о возврате на:

with transaction.commit_on_success():
    return render(request, "template.html", {
        'status': status,
    })

Джанго Плюсы, это правильный подход?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...