On Dec 26, 5:12 pm, Michael Bayer <mike...@zzzcomputing.com> wrote: > On Dec 26, 2011, at 1:50 PM, Kent wrote: > > > Yes, a nice simplification. > > I'm using it to lazyload attributes for objects that aren't in a > > session. I'm not sure if you pointed me there, I think I found it > > myself, but you helped work out the later details... > > Our app lives inside a webserver framework that, very appropriately, > > in my opinion, only has one session for any given web request. So, > > for our framework, I can safely lazyload related attributes for > > transient or detached objects by temporarily setting state.session_id: > > heh yes this is exactly what things used to do between like version 0.2 and > 0.4 :) - if there was no session, it would look in the "threadlocal" context > which somehow was associated with things. This was one of many implicit > decisions/surprises we took out in favor of a behavioral model that's > transparent. For awhile there we also had this crazy "session lookup" > logic that was quasi-optional, nobody knew what it was, and I had similar > endless threads with a few interested folks like yourself trying to > explain/rationalize it, as well as try to decide how this could be an > "optional" hook. The library only improved as I took out all kinds of > tricks like this which were leftovers from the original 0.1 model that > followed the "magic everything, everywhere" pattern. > > various things come to mind in terms of re-enabling this pattern. > > One is that the "session registry" would be extensible. Technically it is > right now if you were to override orm.session._state_session(). Then the > lazyloaders would treat the parent object as though it were inside of > whatever session you had it returning here. You could even hardwire it to > the contextual session itself - then even sharing objects among threads would > be selecting the current thread's session. I could barely consider what > level of surprise side effects might/might not occur from this practice, but > it is how things used to work. > > If you want to play with it, I can re-add some kind of event for this. I > have a name like "detached session" in my mind, maybe an event that's only > called if there's no session_id, maybe it's just a session registry you stick > onto a mapper, though I suppose the event hook is a good place here. > > The other is that the "loader callable" here is a lot like an event and we > could put a hook here that you'd just listen() for. That wouldn't be very > difficult to do, though determining when it's invoked would be tricky. When > the value is locally present in __dict__, I wouldn't want even one function > call of latency there checking for a possible event handler. When the value > isn't present, would the event be before callable_() is invoked, or only if > callable_() is not present, etc. Also i played around with how the > "load_lazy" function could be exposed such that the Session could be > supplied, but it's not very nice for a public API - some loader callables > populate the attribute, others return the function and the AttributeImpl does > the population. Exposing this publicly would mean I can't adjust that > without the risk of breaking existing apps. > > So see what happens if you, for the moment, just monkeypatch over > orm.session._state_session to do a lookup in a global context if > state.session_id isn't set. If that solves the problem of "I want detached > objects to load stuff", for you and everyone else who wants this feature, > then whatever - I'm not at all thrilled about this use case but if it's just > one trivial hook that I don't need to encourage, then there you go. >
Sounds good, I'll look into that. But I'm curious why this approach is preferable over the one I've currently got (callable_ seems more public than _state_session). I admit, it would free my code from the passive logic, since I know you were considering changing those to bit flags, but why is this callable_ approach bad? > > > > > > > > > > def configure_attribute(class_, attr, inst): > > """ > > Set up function to be invoked when relations are 'get'ed on > > possibly > > transient objects, so we can automatically query these related > > objects > > """ > > if isinstance(inst.property, RelationshipProperty): > > default_loader = inst.impl.callable_ > > def load_value(state, passive): > > if state.session_id is not None: > > # this is persistent or pending, so > > # return default sqla functionality > > return default_loader(state, passive) > > if passive is attributes.PASSIVE_NO_FETCH: > > return attributes.PASSIVE_NO_RESULT > > # session_id is currently None > > state.session_id = DBSession().hash_key > > retval = default_loader(state, passive) > > state.session_id = None > > return retval > > inst.impl.callable_ = load_value > > > .. > > .. > > event.listen(DBEntity, 'attribute_instrument', configure_attribute) > > > On Dec 26, 12:26 pm, Michael Bayer <mike...@zzzcomputing.com> wrote: > >> On Dec 26, 2011, at 9:07 AM, Kent wrote: > > >>> Documentation for AttributeImpl.callable_ still reads > >>> "optional function which generates a callable based on a parent > >>> instance, which produces the "default" values for a scalar or > >>> collection attribute when it's first accessed, if not present > >>> already." > >>> But it seems it is no longer the function which generates a callable, but > >>> rather is the callable itself now, accepting both the state and the > >>> passive parameters. > > >>> It used to be two stages, first callable_ accepts a state and then that > >>> returns a callable which accepted the passive parameter. > > >> yes that was a fabulous simplification of things I'm still very happy > >> about. > > >>> Can you briefly summarize how this is meant to work now? (I think the doc > >>> string is wrong now??) > > >> docstring is wrong yes. the callable just receives a state and a passive > >> flag, then loads something for the attribute. It's just one less level > >> of "callable" and the two that we have in use are LoadDeferredColumns and > >> LoadLazyAttribute in strategies.py. This also isn't very public API > >> and if I pointed you to this for some previous issue, I'd be curious if I > >> remembered to mention that..... > > >>> On 12/25/2011 10:31 AM, Michael Bayer wrote: > > >>>> yes a few change names, reconstruct_instance, init_instance, init_failed. > > >>>> On Dec 24, 2011, at 7:42 PM, Kent Bower wrote: > > >>>>> Right. And reconstruct_instance() was renamed load()? > > >>>>> On 12/24/2011 5:56 PM, Michael Bayer wrote: > >>>>>> On Dec 24, 2011, at 10:04 AM, Kent wrote: > > >>>>>>> As the migration guide suggests, I'd like to embrace the events API. > >>>>>>> Is mapper event load() invoked at exactly the same place as the > >>>>>>> deprecated reconstruct_instance() ? > >>>>>> yeah nothing has been moved. All the places where the internals > >>>>>> would call XXXExtension.xxx_event() were just replaced with > >>>>>> self.dispatch.xxx_event(), and the old Extension classes are invoked > >>>>>> via an adapter to the new system. All unit tests for the extension > >>>>>> system remain in place and haven't been modified. > > >>>>>>> -- > >>>>>>> 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 > >>>>> 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 > >>> 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 > > 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.