elixir is adding the "Site" object to the session before the "user=victor"
attribute is set. The setting of user=victor in turn is triggering an
attribute operation on User.site, which is a result of the bidirectional
reference between User and Site. All attributes on User have been
expired since you called session.commit(), and upon next access will be
re-loaded from the database.
If User.sites were one-to-many (more common), SQLA would be stashing the
value in a temporary collection and would not be triggering the load,
however in the case of a scalar reference (User.site), it triggers a load
in order to create a history event. Since Site is already in the session,
the session first flushes any pending changes before loading, and the
error is raised.
It is a TODO within SQLAlchemy for this particular operation to not
trigger an unnecessary load.
However, the biggest issue here is that Elixir is automatically adding
User to the Session, which is a behavior SQLAlchemy has been discouraging
for a couple of years now as it leads to more than one scenario where this
kind of thing happens, so you should check on the Elixir list for how to
disable this.
As far as the loading and the flushing is concerned, that is the result of
the session's expire_on_commit and autoflush behavior both of which are
described in the session documentation.
I've added a patch as well as your test as
http://www.sqlalchemy.org/trac/ticket/1483 to address the unnecessary load
issue. It needs more testing to ensure things don't go wrong, but feel
free to apply said patch to your 0.5.5 copy if you want to experiment.
I'll try to test further this weekend.
OK , somewhat bad news on this front, the test you have needs to do
what its doing. Here's why:
first, make a User with a Site:
v = User(name='victor')
s1 = Site(title='s1')
v.site = s1
sess.add(v)
sess.commit()
everything is fine there. the commit() expires everything.
now make another site, and hook it up.
s2 = Site(title='asdf', user=v)
sess.add(s2)
sess.commit()
when you associate s2 with v, the ORM *must* locate the previous "s1"
row from the database, since you've set this up as one-to-one. the
previous "s1" row needs to be deleted and the ORM only knows that
because the action of setting s2.user hits the User.site backref,
which properly loads the previous row and queues it up for deletion.
the usual use case for "delete-orphan" cascade is on a many-to-one,
its unusual for it to be set on the non-foreign key side of a one-to-
one.
so while I still may do something with 1483, this specific case can't
change.