Search code examples
pythonsqlalchemycontextmanager

SQLAlchemy: Using sessionmaker as contextmanger forces to expunge manually


I'm using SQLAlchemy and trying to use the sessionmaker as a context manager for my transaction (following the documentation):

Session = sessionmaker(some_engine)
with Session.begin() as session:
    query = session.query(SomeTable)
    an_entry = query.one()
    # session.expunge_all()  # Fixes the exception
an_entry.attribute

Which raises an sqlalchemy.orm.exc.DetachedInstanceError: Instance <AnEntry at 0x7f0a9d1c2560> is not bound to a Session exception. This can be solved by expunging inside the contextmanager (see the line commented out above).

I'm surprised by this behavior, as the SQLAlchemy documentation and code indicate that closing a session should automatically expunges all ORM objects. And indeed, the following equivalent of the above works, without me having to manually expunge:

Session = sessionmaker(some_engine)
session = Session()
with session.begin():
    query = session.query(SomeTable)
    an_entry = query.one()
an_entry.attribute

Can anyone explain this behavior?


Solution

  • In the second, "working" example, the context manager is the SessionTransaction object returned by session.begin, not the session itself. The session is not closed before accessing the attribute and therefore there is no DetachedInstanceError: the instance is never expunged.

    A better comparison might be why this code does not raise an error:

    with Session() as s:
        an_entry = s.query(SomeTable).one()
    an_entry.attribute
    

    The reason is that it does not commit. By default, ORM objects are expired when a commit occurs, so a subsequent attribute access requires a query to get the value. In the case of with Session.begin() as session:, the session is committed on leaving the with block, so accessing the attribute requires a query, which requires that the object be attached to a session.

    Session and sessionmaker both accept an expire_on_commit boolean keyword argument to control this behaviour.