SQLAlchemy session: how to keep it alive?

谁说胖子不能爱 提交于 2019-12-23 06:01:08

问题


I have a session object that gets passed around a whole lot and at some point the following lines of code are called (this is unavoidable):

import transaction
transaction.commit()

This renders the session unusable (by closing it I think).

My question is two part:

  1. How do I check if a session is still alive and well?
  2. Is there a quick way to revitalize a dead session?

For 2: The only way I currently know is to use sqlalchemy.orm.scoped_session, then call query(...)get(id) many times to recreate the necessary model instances but this seems pretty darn inefficient.

EDIT

Here's an example of the sequence of events that causes the error:

modelInstance = DBSession.query(ModelClass).first()
import transaction
transaction.commit()
modelInstance.some_relationship

And here is the error:

sqlalchemy.orm.exc.DetachedInstanceError: Parent instance <CategoryNode at 0x7fdc4c4b3110> is not bound to a Session; lazy load operation of attribute 'children' cannot proceed

I don't really want to turn off lazy loading.

EDIT

DBSession.is_active seems to be no indication of whether or not the session is in fact alive and well in this case:

transaction.commit()
print(DBSession.is_active)

this prints True...

EDIT This seemed too big for a comment so I'm putting it here.

zzzeek said: "An expired object will automatically load new state from the database, via the Session, as soon as you access anything on it, so there's no need to tell the Session to do anything here."

So how do I get stuff committed in such a way that this will happen? calling transaction.commit is wrong, what's the correct way?


回答1:


so the first thing to observe here is "import transaction" is a package called zope.transaction. this is a generic transaction that takes hold of any number of sub-tasks, of which the SQLAlchemy Session is one of them, via the zope.sqlalchemy extension.

What zope.sqlalchemy here is going to do is call the begin()/rollback()/commit() methods of the Session itself, in response to it's own management of the "transaction".

The Session itself works in such a way that it is almost always ready for use, even if its internal transaction has been committed. When this happens, the Session upon next use just keeps going, either starting a new transaction if it's in autocommit=False, or if autocommit=True it continues in "autocommit" mode. Basically it is auto-revitalizing.

The one time that the Session is not able to proceed is if a flush has failed, and the rollback() method has not been called, which, when in autocommit=False mode, the Session would like you do to explicitly when flush() fails. To see if the Session is in this specific state, the session.is_active property will return False in that case.

I'm not 100% sure what the implications are of continuing to use the Session when zope.transaction is in use. I think it depends on how you're using zope.transaction in the bigger scheme.

Which leads us where lots of these questions do, which is what are you really trying to do. Like, "recreate the necessary model instances" is not something the Session does, unless you are referring to existing instances which have been expired (their guts emptied out). An expired object will automatically load new state from the database, via the Session, as soon as you access anything on it, so there's no need to tell the Session to do anything here.

It's of course an option to even turn off auto-expiration entirely, but that you are even arriving at a problem here implies something is not working as it should. Like there's some error message you're getting. More detail would be needed to understand exactly what the issue you're having is.



来源:https://stackoverflow.com/questions/12725916/sqlalchemy-session-how-to-keep-it-alive

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!