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.

Reply via email to