2011-04-13 27 views
30

Tengo una función de vista:Transacción bloque logrado terminó con la espera de 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, 
    }) 

creo que cada ruta de código termina la transacción de una manera u otra. Pero Django parece quejarse de que no. ¿Algunas ideas?

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

EDITAR: No se han lanzado otras excepciones para modificar la ruta del código.

+0

¿El ABC está definido en otra parte? –

+0

¿estás usando postgres? Estos pueden ser relevantes: [aquí] (http://groups.google.com/group/django-users/browse_thread/thread/9b85e45d8fc015d2) y [aquí] (https://groups.google.com/group/django- cms/browse_thread/thread/3f8b1c10faa773f3/dddbc3b93b658e80? # dddbc3b93b658e80) – DTing

+1

Sí, ABC se define lo siento. ¡Sobre la limpieza entusiasta de la fuente! – Joe

Respuesta

83

Después de obtener un problema similar y perder horas en él, descubrí cómo depurar esta situación.

Por alguna razón, @ transaction.commit_manually decorator silencia las excepciones que ocurren en la función.

Elimine temporalmente el decorador de su función, ahora verá la excepción, arréglenlo y devuelva el decorador.

+1

Este consejo es perfecto! ese @ transaction.commit_manually decorator estaba comiendo el verdadero problema. – boatcoder

+0

También me salvaste muchas horas. Este comportamiento es muy extraño. Estoy usando commit_on_success decorator y no se comporta de la misma manera. – duduklein

+0

Lo extraño es que al hacer un 1/0 se muestra una excepción correcta :( –

0

Estaba teniendo el mismo problema e intenté varios enfoques. Esto es lo que funcionó para mí, pero no estoy seguro de si esta es la manera correcta de hacerlo. Cambie su declaración de devolución a:

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

Django Pros, ¿este es el enfoque correcto?

1

Esto siempre ocurre cuando ocurre una excepción no controlada en algún lugar del código. En mi caso, por alguna razón, la excepción no fue lanzada al depurador, que es lo que me causó la confusión.

+0

Esto es lo que sospecho. Pero si hay una excepción, no puedo verla, ¡y lo he intentado! Al final, decidí resolver el problema de una manera diferente a las transacciones, y con mi nuevo enfoque no veo la excepción que esperaba ver. – Joe

9

Tuve el mismo problema. La única solución que encontré fue usar una cláusula try/finally para asegurar que se produzca una confirmación después de el renderizado.

@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 

No tiene sentido, pero funcionó para mí.

+0

Esto funcionó para mí. Además, descubrí que si quería retrotraer como parte de un flujo normal (no una excepción), entonces tenía que crear una excepción (x = 1/0) para asegurarme de que terminé en un final con una reversión. Tal vez porque toda mi función está en un try catch. – user984003

+3

Esta es una buena solución, pero en lugar de usar la variable 'commited 'puedes usar' transaction.is_dirty() '. si puede, verifique el middleware de transacción django, ellos hacen exactamente lo mismo. – Hassek

1

tuve problema similar, tal vez este código funciona bien para usted:

@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, 
    }) 
2

que tenían el mismo problema y se enteraron de que incluso si cierra correctamente la transacción manualmente en caso de excepciones, si a continuación, escribir a De nuevo, el orm dentro del alcance de la transacción manual, parece reabrir la transacción de alguna manera y causa la excepción de transacción.

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

otra razón por la que puede estar viendo este problema es cuando tiene múltiples db en el sistema.

que fue capaz de superar este error con

@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') 
0

Ponga su código en bloque try/except .En excepto bloque de sólo hacer una transaction.rollback y registrar el objeto de excepción.

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

¿Estás seguro de que esto es compatible con los puntos planteados por la respuesta aceptada? – Joe

1

Como otras personas han dicho, las excepciones que se producen dentro de la función decorada se "pierden" porque son sobrescritos por el TransactionManagementError excepción.

Propongo ampliar el decorador transaction.commit_manually.Mi decorador transaction_commit_manually usa el decorador transaction.commit_manually internamente; si se produce una excepción en la función decorada, mi decorador detecta la excepción, realiza un transaction.rollback() y vuelve a generar la excepción. Por lo tanto, la transacción se borra correctamente y la excepción original no se pierde.

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) 
Cuestiones relacionadas