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.



> 
> 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 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 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