web-dev-qa-db-ja.com

トランザクション管理ブロックは、保留中のCOMMIT / ROLLBACKで終了しました

私はビュー機能を持っています:

@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はそうではないと不平を言っているようです。何かアイデアはありますか?

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

編集:コードパスを変更するために他の例外はスローされていません。

31
Joe

同様の問題が発生し、それに何時間も費やした後、私はこの状況をデバッグする方法を見つけました。

何らかの理由で、@ transaction.commit_manuallyデコレータは関数で発生する例外を沈黙させます。

関数からデコレータを一時的に削除すると、例外が表示され、修正してデコレータを元に戻します。

85
Gert Steyn

私も同じ問題を抱えていました。私が見つけた唯一の解決策は、try/final句を使用して、コミットが確実に行われるようにすることでしたafterレンダリング。

@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

意味がありませんが、私にとってはうまくいきました。

9
Cerin

同じ問題が発生し、例外が発生した場合に手動でトランザクションを適切に閉じても、手動トランザクションスコープ内で再度ormに書き込むと、何らかの理由でトランザクションが再開され、トランザクション例外が発生するように見えることを学びました。

            with transaction.commit_manually():
                try:
                    <exciting stuff>
                    transaction.commit()                        
                except Exception, e:
                    transaction.rollback()
                    o.error='failed' <== caused transaction exception
2
Paul Bormans

この問題が発生する可能性があるもう1つの理由は、システムに複数のデータベースがある場合です。

私はこのエラーを克服することができました

@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
srj

私は同様の問題を抱えていました、多分このコードはあなたのためにうまくいくでしょう:

@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
Griffosx

他の人が言っているように、decorated関数内で発生する例外は、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
Luca

これは、未処理の例外がコードのどこかで発生した場合に常に発生します。私の場合、何らかの理由で、例外がデバッガーにスローされなかったため、混乱が生じました。

1
Rob

私は同じ問題を抱えていて、さまざまなアプローチを試しました。これが私のために働いたものですが、これがそれを行う正しい方法であるかどうかはわかりません。 returnステートメントを次のように変更します。

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

Django Pros、これは正しいアプローチですか?

0
Anjan

コードをtry/exceptブロックに配置します。exceptブロックでは、transaction.rollbackを実行し、例外オブジェクトをログに記録します。

@transaction.commit_manually
def xyz(xyz):
   try:
       some_logic
       transaction.commit()
    except Exception,e:
       transaction.rollback()
       print str(e)
0
Joy

必要に応じてデータベースのバックアップを取り、テーブルを削除しますROLLBACK_TESTデータベースから。

mysql> DROP TABLE `ROLLBACK_TEST`;
0
Dinesh Patil