On Oct 24, 2013, at 4:40 PM, Daniel Grace <thisgenericn...@gmail.com> wrote:

> The problem I was encountering is that history doesn't make it easy to 
> differentiate between collections and non-collections very well:
> 
> inspect(obj).attrs.intcolumn.history.unchanged returns return [intval], 
> rather than intval.
> 
> inspect(obj).attrs.children.history.unchanged returns [<childobj>]

the rationale for this is because the history API was first and foremost 
designed for internals, where it iterates through these collections without 
regard for whether the collection is a many-to-one or one-to-many.   If you 
want to distinguish between scalars and collections, this could be added to 
attribute state directly but for now you can look at 
state.manager[key].collection, which is a boolean.


> 
> It'd be handy if I can just do something like:
> 
> original = get_original(self)
> if original.value != self.value:
>     pass

history.has_changes() tells you the equivalent of if the “old” and “new” values 
are different.


> With some sufficiently-robust-but-should-be-simple implementation of 
> get_history.

there’s many “simple” implementations that wouldn’t do what everyone needs - 
the API you want might not accomplish what someone else wants - which means if 
we rush ahead and implement a “simple” API that doesn’t take enough use cases 
into account, we get two apis instead of one, and new users will still want 
more APIs - this is how confusing and inconsistent APIs are produced, e.g. 
piecemeal in response to individual requests, rather than designing for a 
bigger picture.   The existing API is actually the “simple-yet-robust” 
implementation for what this system was originally designed for.    

Once I have dozens of people asking me for the same thing, or different things 
for which I can discern exactly what everyone really needs (e.g. when the “big 
picture” is very clear), then the “simple” API becomes apparent.  We’re not 
there yet, sorry.  We’re only at the “it’s possible” phase on attribute history.

> The main problem with Jonathan's approach is the results will be inconsistent 
> if any of the attributes are collections.

well his approach is iterating through the table’s columns, which is of limited 
use.  The loop I illustrated iterates through all mapped attributes including 
collections which takes more into account.

If you care to illustrate a function with clear inputs and outputs, I can work 
up whatever kind of implementation it needs internally for you, or at least 
point out those areas where more design is needed (such as how to generate 
“diffs” of collections).   

> 
> Michael: By "Losing changes", I mean that I know it's easily possible to get 
> to the unmodified version of an object via session.rollback() or 
> session.expire() -- but then I lose the pending (not yet committed) changes 
> that I'm trying to compare against.  I wasn't referring to auto flush.

Well if you are looking to actually re-construct the entire object graph from 
memory as it was before the flush,  this is something I looked into with great 
detail some years ago and determined it would be enormously complex to do 
completely - it is of course simple for scalar attributes but once you start 
dealing with collections and bidirectional connections between objects, 
reproducing the full state of objects prior to user and within-flush 
manipulation of state becomes a major task, which also leans towards adding a 
lot more overhead to the history tracking system in the first place which 
starts to degrade performance even further than the instrumentation already 
does.
 
If you want to maintain your own “diff” of changes as they are produced, which 
is entirely independent of flush(), you can do this - just implement attribute 
event listening for all attributes, where you’ll be able to log every change to 
an object graph into the format that you need.   This is not trivial but you’d 
have full control over things.   Instrumentation for all attributes is 
illustrated in the examples/custom_attributes/listen_for_events.py example.




-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to