On Oct 2, 2012, at 3:49 PM, Mark Friedenbach wrote:
> Well it's hard to boil it down to a specific test case, as it affects the underlying assumptions that went into the design of ORM-tree. Here's an explanation of what I'm doing, and perhaps you can tell me if I'm (ab)using the API correctly:
>
> The meat of the code is a mapper extension whose insert, update, and delete hooks execute SQL expressions directly to update the nested-interval tree parameters. For efficiency I use the SQL expression layer and then manually update the working set of ORM objects to reflect the new state.
>
> In essence:
>
> connection.execute(...update in sql expression language...)
> for obj in session.identity_map:
> ...same update, as python...
>
> (The session.identity_map is accessible to the mapper extension because it was tucked away as a hidden attribute in the object in a session extension before_flush handler.)
>
> As far as I can tell, the update to the session objects is now triggering a 2nd flush, even though the purpose of the update was to refresh the objects with their current database values. On the 2nd flush the SQL expression updates get executed again, resulting in a corrupt database.
>
> Any red flags in what I'm doing?
Basically, when the flush is finished, session.dirty,
session.new and session.deleted should be empty. If you add a quick after_flush_postexec() event, and inside the event assert that these are all empty, then you shouldn't get any subsequent flush.
If session.dirty is acquiring state due to attribute set events, as I see a lot of setattr() going on, you can set attributes without establishing history using set_committed_value():
http://docs.sqlalchemy.org/en/rel_0_7/orm/session.html#sqlalchemy.orm.attributes.set_committed_value . However, if that's in fact what's happening, I might need to look into that because the "dirty" state of attributes should be unconditionally reset before the flush completes. I just did a test to confirm that, so I'd be curious to see a test (unless I can come up with one here) that illustrates plain setattr() of a column-based attribute causing a second flush. In theory it would be only .new and .dirty that might have state after the flush completes.