DetachedInstanceError:实例<tcustomer at =“”0x428ddf0 =“”>未绑定到会话</tcustomer>

时间:2014-05-30 19:12:37

标签: python pyramid

我已经查看了之前有类似问题的人的例子,但我似乎无法弄清楚为什么我的工作会因为我使用相同的会话而无法工作。

这是我的代码块,它只是查询数据库以查看客户是否存在,然后在另一个插入中使用该客户。但是一旦我点击transaction.commit()行,它就会抛出DetachedInstanceError。这可能来自早期transaction.commit()的另一种方法吗?

@view_config(route_name='create_location', renderer='templates/main.html')
def create_location(request):

    #get the json data from the ajax call
    data = request.json_body

    session = DBSession

    locationName = data["location"]
    custID = data["custID"]

    Customer = session.query(TCustomer).filter(TCustomer.ixCustomer==custID).one()

    #create customer
    Location = TLocation(sDescription=locationName, ixCustomer=Customer)
    session.add(Location)
    session.flush()
    transaction.commit()

    return Response('ok')

这是我的DBSession:

DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))

2 个答案:

答案 0 :(得分:2)

  

这可能来自早期的另一种方法中的transaction.commit()吗?

是,如果在同一请求 - 响应周期内调用另一个方法 - 一旦提交了事务,SQLAlchemy就不能保证任何内存中的ORM对象仍然代表实际数据库的状态,因此您不能仅在一个事务中获取对象并将其保存回另一个事务中,而无需将现在分离的对象显式合并到新会话中。

您通常不应该在代码中使用transaction.commit()使用ZopeTransactionExtension背后的想法是将SQLAlchemy事务与Pyramid的请求 - 响应周期联系起来 - a在请求启动时构造新会话,并在请求成功时提交,或者在请求失败时回滚(即在视图中引发异常)。在您的代码中,您不应该关心提交任何内容 - 只需将新对象添加到会话中:

@view_config(route_name='create_location', renderer='templates/main.html')
def create_location(request):

    #get the json data from the ajax call
    data = request.json_body

    customer = DBSession.query(Customer).filter(Customer.id==data["custID"]).one()
    session.add(Location(description=data["location"], customer=customer))

    return Response('ok')

(无法抗拒使代码更像“正常”的Python代码...匈牙利符号是......错误...这些天不常用...感谢给我怀旧的闪回:)))有关详细信息,请参阅PEP 8

罕见的情况下,您可能希望请求的某些部分无论如何都能成功,甚至只有在发生错误时才将数据保存到数据库(将错误记录到数据库可能是例)。在这些情况下,您使用单独的会话,该会话在没有ZopeTransactionExtension的情况下进行配置。您需要手动提交此类会话:

try:
    do_something_which_might_fail()
except Exception as e:
    session = ErrorLogSession()
    session.add(SomeORMObject(message=format_exception(e))
    session.commit()

进一步阅读:

  • When do I construct a Session, when do I commit it, and when do I close it? - SQLAlchemy文档中的高级概述。 “TL; DR:作为一般规则,将会话的生命周期与访问和/或操作数据库数据的函数和对象分开并保持外部。”请注意,Pyramid已经为您完成了所有会话管理 - 分别从您的代码外部结束。

  • Databases Using SQLAlchemy - 一个使用SQLAlchemy的基本Pyramid应用程序。有关将一些数据保存到数据库的典型代码示例,请参阅dev wikipage_add()函数。

答案 1 :(得分:0)

以下是解决方案:

DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension(keep_session=True)))

必须添加keep_session=True

然后我只需要使用session = DBSession()进行查询和会话工作,而不是执行DBSession