On Jun 18, 2009, at 2:19 PM, millerdev wrote:

> This would be done by the loader strategy (probably a variant of
> LazyLoader), which would issue a single query. The result of that
> query would be used to populate the attributes collection of each item
> on the order.
>
>>

hey Daniel -

Good to have you back on the list.

So this is some variant of, "i have a bunch of objects and I'd like to  
issue a single SQL statement representing a set of collections across  
all of them".  SQLA doesn't have a built in "loader" function like  
this, although its been discussed.  IMO it's just too much to be going  
on automatically with too little understanding required up front,  
meaning end users might flip on "super-magic-loading" and then wonder  
why some massive query is being triggered by a single attribute load -  
if they had already discarded half their objects that would be the  
recipients then the query would in fact be quite wasteful.    The  
controversial part is that you ask for a single attribute on a single  
object, but then data is loaded into objects that are "elsewhere" as a  
result.

I think Hibernate might have this kind of loading available, I've seen  
it in their docs but I doubt its used very much.  If it were a highly  
requested feature that would see a lot of use, that would help its  
case...otherwise it's a complicated feature that's hard to implement,  
maintain and support to the degree that a core SQLA feature requires,  
mostly in terms of constructing the right SQL for a huge variety of  
cases, writing/maintaining all the unit tests for those cases, and  
supporting on the mailing list and IRC those users who think they want  
to use this feature but don't really understand it (or are hitting  
bugs with it).

OTOH, rolling recipes for things like this yourself has no such burden  
of working perfectly in all cases at all times for everyone  
everywhere, it only needs to work for your immediate needs.   I think  
the infrastructure exists to construct this feature seamlessly without  
the need to hack into LoaderStrategy (though that would be where a  
core version of this feature would be created).

You'd start using plain session.query() to get the rows you want, hash  
together the collections as desired, and then apply them to the  
already loaded objects using the attributes.set_committed_value()  
function which is used for this purpose (pseudocode follows)....(also  
use trunk rev 6066 since I had to fix set_committed_value()):

         from sqlalchemy.orm import attributes

         loaded_collections = collections.defaultdict(list)

        for parent_pk, obj in session.query(Parent.id, DesiredClass).<figure  
out the correct criteria>:
             collection = loaded_collections[parent_pk]
             collection.append(obj)

        for parent_pk, collection in loaded_collections.iteritems():
            object = lookup_the_parent_object(parent_pk)  # probably  
via session.identity_map
            attributes.set_committed_value(object, "collectionname",  
collection)

note that set_committed_value() blows away any pending changes on the  
collection.  If that's an issue, you can perform a conditional which  
checks for an already existing collection in object.__dict__, or use  
attributes.get_history(), etc.

Since you're loading for every object in the result, it seems  
reasonable that you'd just issue the above query immediately following  
the primary result (when we've considered this feature, that's how we  
considered it).  But if you truly wanted to trigger the above by a  
"lazy" attribute, you can roll the above into a callable, and apply to  
any attribute via:

      def load_data():
          < the above loading logic>
          return attributes.ATTR_WAS_SET

      state = attrbutes.instance_state(some_object)
      state.set_callable("collectionname", load_data)

which means, when you hit some_object.collectionname, load_data is  
called, and the return value of ATTR_WAS_SET indicates the callable  
populated what it needed, no need for attributes to use the return  
value as the collection.

So you could build a subclass of Query which applied all of the above  
using a function like ".special_deferred_load(collectionname)", which  
would then install the "loader callable" to each element in the result  
when that option is selected.    The hard part is what I've left out,  
i.e. constructing the query to load the child items and finding all  
the parent objects that are involved in the load.




--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to