I think I understand why, during a flush(), if I use
session.query().get() for an item that was just added during this flush,
I don't get the persistent object I might expect because the session
still has it as pending even though, logically, it is already persistent.
I don't suppose you have any desire to support that, huh? The use case
would be related to the future ticket
http://www.sqlalchemy.org/trac/ticket/1939 (and
http://www.sqlalchemy.org/trac/ticket/2350).
Attached is a script demonstrating the issue I've hit. I can work
around it with some difficulty, but I wanted your input and thoughts.
Thanks,
Kent
--
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at
http://groups.google.com/group/sqlalchemy?hl=en.
# Show what happens when we use session.query().get()
# during a flush to load an object that is being inserted during the same flush
# Instead of getting what was the pending object, we get a new copy of what
# the orm thinks is persistent and then it is detached after the flush finishes
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy import event
from sqlalchemy.orm.util import has_identity
engine = create_engine('sqlite:///', echo=True)
metadata = MetaData(engine)
Session = sessionmaker(bind=engine)
rocks_table = Table("rocks", metadata,
Column("id", Integer, primary_key=True),
)
bugs_table = Table("bugs", metadata,
Column("id", Integer, primary_key=True),
Column("rockid", Integer, ForeignKey('rocks.id'),),
)
class Rock(object):
def __repr__(self):
return '<Rock@%d: id=[%s] in session:[%s] has_identity[%s]>' % (id(self), self.__dict__.get('id'), self in session, has_identity(self))
class Bug(object):
def __repr__(self):
return '<Bug@%d: id=[%s] rockid[%s] with rock[%s]>' % (id(self), self.__dict__.get('id'), self.__dict__.get('rockid'), self.__dict__.get('rock','not set'))
class BugAgent(MapperExtension):
def before_update(self, mapper, connection, instance):
assert 'rock' not in instance.__dict__
print "\n\n**** during flush"
# after http://www.sqlalchemy.org/trac/ticket/2350, we could just reference like this:
#instance.rock
instance.rock = session.query(Rock).get(instance.rockid)
#
# we just loaded a Rock that was just inserted during this flush, so
# it looks persistent to the orm, but the orm also has this object
# already (still pending). After the flush is done,
# the pending object will be the only one in the session and the
# object we just loaded here will be removed from the session (detached)
#
print "\n\n*****before_update: %r\n" % instance
assert 'rock' in instance.__dict__
mapper(Rock, rocks_table,
properties={'bugs': relationship(Bug,
cascade='all,delete-orphan',
backref='rock')
})
mapper(Bug, bugs_table, extension=BugAgent())
@event.listens_for(Bug.rockid, 'set')
def autoexpire_rock_attribute(instance, value, oldvalue, initiator):
if value != oldvalue:
if instance in session and has_identity(instance):
assert 'rock' in instance.__dict__
print "\n\n****Bug.rockid changing from [%s] to [%s]..." % (oldvalue, value)
print "******about to expire rock for %r" % instance
session.expire(instance, ['rock'])
print "******expired rock for %r\n" % instance
assert 'rock' not in instance.__dict__
metadata.create_all()
session = Session()
# add a rock and bug
rock=Rock()
rock.id = 0
bug=Bug()
bug.id = 0
rock.bugs.append(bug)
session.add(rock)
session.commit()
# later... new session
session = Session()
b1 = Bug()
b1.id = 0
rock=Rock()
rock.id = 1
rock.bugs.append(b1)
print "\n\n****merge start\n"
merged = session.merge(rock)
print "\n\n****merge end\n"
print "****flush\n"
session.flush()
assert 'rock' in merged.bugs[0].__dict__
# show that the pending object has become persistent
print "\n\nsession's pending obj turned persistent: %r" % session.query(Rock).get(1)
# show that the object we loaded has been detached from the session
print 'merged.bugs[0].rock (copy of same object, no longer in session): %r' % merged.bugs[0].rock