I think I've got a strategy that will work and doesn't mean hacking merge()...
Thanks for your input, if you have inspiration, I'll still gladly hear it. On Apr 30, 8:18 am, Kent Bower <k...@retailarchitects.com> wrote: > Thanks in advance for the discussion, hope I can continue without you > feeling I'm taking too much of your time... > > You said > """ > If you are using merge(), and you're trying to use it to figure out > "what's changed", that implies that you are starting with a business > object containing the "old" data and are populating it with the "new" data. > """ > > This isn't quite the case. Rather, I am handed an object (actually JSON > text representation of an object) how it should look *now*. I parse > that object and create a transient SQLA object with the exact same > properties and relations - recursively, very similar to what merge() is > doing. > > Then I hand that transient object to merge(), who nicely loads the > original from the database - if it existed -, or creates it if it didn't > exist - and attaches it to the session for me. What I am missing is > "what did it look like before". Certainly, I could call > session.query.get() before merge() so I would know what it looks like, > but the main problem with that is matching up all the related objects > with the post-merged ones, esp for list relations. > Plus, merge() is already looking up these objects for me, so it seemed > smarter to harness what merge is already doing. > > I've looked into the examples you provided. Waiting for before_flush() > won't work for our use case since we need the "old" "_original" object > available *before* the flush as well as after. I could...before the > flush, use "attributes.get_history()," but it quickly becomes > problematic if the programmer needs to look in two completely different > places, both "attributes.get_history()" for post-flush changes and also > "_original" for pre-flush changes... somewhat defeats the purpose. > > I was considering that a SessionExtension def before_merge(session, > merged) hook would be handy for me... one that is called before the > properties are updated on "merged". Inside that hook, I could > potentially clone merged and set it as an "_original" to itself and > check if it was newly created by checking session.new (or new_instance > boolean could be passed to the hook) > > However, in the end, I would *still* have the problem that, yes, each > post-merge object has a detached reference to "_original", but these > cloned objects' relations would not be populated correctly among each > other.... so I couldn't call "order._original.calc_points()" and expect > the relations to be populated correctly.... that's what I'm trying to > figure out how to accomplish.. that would be ideal. Almost like an > "alternate universe" snapshot in time of the object (now detached) > before changes with all relations still populated and where each > *current* object has reference to its "old" version. > > Could I look it up in a separate session and then merge it into my main > session? Then, after that, merge the transient objects into my main > session? Would that get me closer? Ideally, I wouldn't want it to need > 2 trips to the database. > > On 4/29/2010 4:48 PM, Michael Bayer wrote: > > > > > Kent Bower wrote: > > >> It is helpful to know what SQLA was designed for. > > >> Also, you may be interested to know of our project as we are apparently > >> stretching SQLA's use case/design. We are implementing a RESTful web > >> app (using TurboGears) on an already existent legacy database. Since > >> our webservice calls (and TurboGears' in general, I believe) are > >> completely state-less, I think that precludes my being able to harness > >> "descriptors, custom collections, and/or AttributeListeners, and to > >> trigger the desired calculations before things change" because I'm just > >> handed the 'new' version of the object. > >> This is precisely why so much of my attention has been on merge() > >> because it, for the most part, works that out magically. > > > If you are using merge(), and you're trying to use it to figure out > > "what's changed", that implies that you are starting with a business > > object containing the "old" data and are populating it with the "new" > > data. Custom collection classes configured on relationship() and > > AttributeListeners on any attribute are both invoked during the merge() > > process (but external descriptors notably are not). > > > merge() is nothing more than a hierarchical attribute-copying procedure > > which can be implemented externally. The only advantages merge() itself > > offers are the optimized "load=False" option which you aren't using here, > > support for objects that aren't hashable on identity (a rare use case), > > and overall a slightly more "inlined" approach that removes a small amount > > of public method overhead. > > > You also should be able to even recreate the "_original" object from > > attribute history before a flush occurs. This is what the "versioned" > > example is doing for scalar attributes. > > -- > You received this message because you are subscribed to the Google Groups > "sqlalchemy" group. > To post to this group, send email to sqlalch...@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 sqlalch...@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.