2011-04-13 58 views
30

我有一个观点函数结束。但是Django似乎在抱怨它没有。有任何想法吗?事务管理块未决的COMMIT/ROLLBACK

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

编辑:没有其他异常正在抛出来改变代码路径。

+0

“ABC”是否定义在其他地方? – 2011-04-13 11:32:25

+0

你在使用postgres吗?这些可能是相关的:[这里](http://groups.google.com/group/django-users/browse_thread/thread/9b85e45d8fc015d2)和[这里](https://groups.google.com/group/django- cms/browse_thread/thread/3f8b1c10faa773f3/dddbc3b93b658e80?#dddbc3b93b658e80) – DTing 2011-04-13 12:08:03

+1

是的,ABC定义为抱歉。在热情的来源清洁! – Joe 2011-04-13 12:57:17

回答

83

在得到类似的问题并浪费了几个小时后,我想出了如何调试这种情况。

由于某种原因,@ transaction.commit_manually装饰器会使发生在函数中的异常无效。

暂时从您的函数中删除装饰器,您现在将看到异常,修复它并将装饰器放回!

+1

这个建议是现货! @ transaction.commit_manually装饰者正在吃掉真正的问题。 – boatcoder 2011-12-21 03:56:04

+0

你也为我节省了很多时间。这种行为很奇怪。我正在使用commit_on_success装饰器,它的行为方式不同。 – duduklein 2012-06-14 14:50:23

+0

奇怪的是,做1/0显示正确的异常:( – 2012-07-20 12:40:39

0

我遇到了同样的问题,并尝试过各种方法。这是对我有用的东西,但我不确定这是否是正确的做法。将您的退货声明更改为:

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

Django Pros,这是正确的做法吗?

1

当代码中发生未处理的异常时,总会发生这种情况。在我的情况下,出于某种原因,这个例外情况并没有引发给调试器,这对我造成了困惑。

+0

这是我怀疑的。但是,如果有例外,我无法看到它,我已经尝试过了!最后,我决定用不同的方式解决交易问题,而用我的新方法,我没有看到我期望看到的例外。 – Joe 2011-04-15 15:35:58

9

我有同样的问题。我发现的唯一解决方案是使用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 

没有意义,但它对我有用。

+0

这对我有效。另外,我发现如果我想作为正常流程的一部分进行回滚(而不是例外),那么我必须创建一个异常(x = 1/0)来确保最终以回滚结束。也许是因为我的整个功能都处于试验阶段。 – user984003 2012-10-22 12:57:34

+3

这是一个很好的解决方案,但是可以使用'transaction.is_dirty()'来代替使用'commited'变量。如果可以的话,请检查django事务中间件,它们完全相同。 – Hassek 2014-01-30 00:33:10

1

我有类似的问题,也许这个代码工作正常,为您提供:

@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

我有同样的问题,得知即使你正确手动关闭交易中的异常的情况下,如果再写入在手动事务范围内再次使用orm,似乎以某种方式重新打开事务并导致事务异常。

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

您可能会看到此问题的另一个原因是系统中有多个db。

我是能够克服这种错误与

@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

把你的代码中的try/except块。在拦网除外只是做了transaction.rollback并记录异常对象。

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

您确定这是与被接受的答案提出的要点兼容吗? – Joe 2015-02-27 17:20:06

1

正如其他人所说,装饰功能中发生的异常“丢失”,因为它们是由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)