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.

Reply via email to