Thanks Michael, the 'solution' is seems was to refactor my code so that operations are clearly performed in the correct order, and in some cases to explicitly load and/or update the tree parameters when they might have changed. Now I'm not only doing before_flush (which I've restricted to session/ORM queries only), but also before/after insert, update, and delete (where I only do SQL expression queries).
On Feb 4, 8:52 am, Michael Bayer <mike...@zzzcomputing.com> wrote: > 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%... > > 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_c... > > 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 sqlalchemy@googlegroups.com. > > To unsubscribe from this group, send email to > > sqlalchemy+unsubscr...@googlegroups.com. > > For more options, visit this group > > athttp://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 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.