On Feb 4, 2012, at 11:23 AM, Mark Friedenbach wrote: > Hi, I'm running into a problem with my nested sets implementation. > Inserting, moving, or removing a node can potentially affect one or > more of many other nodes' tree properties (tree id, left, right, > depth, or parent relationship). For efficiency's sake this change > occurs as a single, rather complex SQL expression query that handles > the magic of updating all the other node values. > > Just as a precaution I've added a session.expire_all() after the > session.execute(<query>), so that the tree values will be reloaded as > I move on to process other node operations in the same transaction. > However what I've discovered is that expire_all() causes *all* as-of- > yet unpersisted changes to be lost. As an example of what I mean, > here's an actual shell log: > >>>> obj = session.query(...) >>>> obj.name > u'root1' >>>> obj.name = 'root66' >>>> session.add(obj) >>>> session.expire_all() >>>> session.commit() >>>> obj.name > u'root1' > > It may be possible that I can restructure the order in which I do > things so that stale data isn't an issue. But out of curiosity, is > there a way to expire only *unchanged* stale data? This is how I > naïvely expected expire_all() to work.
"all" means everything, that method is called typically after rollback() or commit() in conjunction with the transaction. While there is a way to detect history on all attributes and expire just those with no net change, this is a time consuming operation and should not be necessary. In this case, you know that the only values that are being updated outside of the normal flush process are the "left" and "right" columns (and whatever denormalized data you're storing such as "depth"), so you should just be expiring those, and it should be either within the after_flush event: http://docs.sqlalchemy.org/en/latest/orm/events.html?highlight=after%20flush#sqlalchemy.orm.events.SessionEvents.after_flush These attributes will refresh themselves when next accessed. Or if you have a means available of populating some of these attributes with their correct value instead of just expiring, you can use attributes.set_committed_value(): http://docs.sqlalchemy.org/en/latest/orm/session.html?highlight=set_committed_value#sqlalchemy.orm.attributes.set_committed_value Reading your paragraph again, if you're actually doing the math for left/right/depth in Python and need the value of those attributes to be correct as the flush() proceeds, I'd consider doing the math in SQL, as you can't assume all the nodes are going to be loaded into memory. > Alternatively, a good API for this case would have been an > expire_all(mapped_class, ['attribute', 'names']), a sort of compromise > between expire() and expire_all(). You can roll this yourself: for obj in session.identity_map.values(): if isinstance(obj, myclass): session.expire(obj, ['a', 'b']) > > -- > You received this message because you are subscribed to the Google Groups > "sqlalchemy" group. > To post to this group, send email to [email protected]. > To unsubscribe from this group, send email to > [email protected]. > For more options, visit this group at > http://groups.google.com/group/sqlalchemy?hl=en. > -- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.
