A simple script to reproduce
import transaction
from sqlalchemy import create_engine
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy.orm import scoped_session
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from zope.sqlalchemy import ZopeTransactionExtension
engine = create_engine('sqlite:///')
session = scoped_session(sessionmaker(
autocommit=False,
autoflush=False,
bind=engine,
extension=ZopeTransactionExtension(keep_session=True)
))
Base = declarative_base()
class Foobar(Base):
__tablename__ = 'foobar'
id = Column(Integer, primary_key=True)
name = Column(String)
Base.metadata.bind = engine
Base.metadata.create_all()
with transaction.manager:
foobar = Foobar(id=1, name='john')
session.add(foobar)
foobar = session.query(Foobar).get(1)
with transaction.manager:
foobar.name = 'jackson'
session.flush()
# boom! sqlalchemy.orm.exc.DetachedInstanceError will be raised
print foobar.name
After looking into the code, this is caused by join_transaction in mark_changed method doesn't take keep_session and pass along properly.
A simple script to reproduce
After looking into the code, this is caused by
join_transactioninmark_changedmethod doesn't takekeep_sessionand pass along properly.