From: Mike Bayer Date: Mon, 22 Mar 2010 17:38:44 +0000 (-0400) Subject: - merge from tip X-Git-Tag: rel_0_6beta3~12^2~37 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6dfa26a9a249230bc108c508e0d5cd725c79d11a;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - merge from tip - refine subq loading to use the same approach for m2m/o2m --- 6dfa26a9a249230bc108c508e0d5cd725c79d11a diff --cc lib/sqlalchemy/orm/interfaces.py index 579101f0d8,579101f0d8..03ebb97c4f --- a/lib/sqlalchemy/orm/interfaces.py +++ b/lib/sqlalchemy/orm/interfaces.py @@@ -482,6 -482,6 +482,12 @@@ class MapperProperty(object) self.do_init() self._compile_finished = True ++ @property ++ def class_attribute(self): ++ """Return the class-bound descriptor corresponding to this MapperProperty.""" ++ ++ return getattr(self.parent.class_, self.key) ++ def do_init(self): """Perform subclass-specific initialization post-mapper-creation steps. diff --cc lib/sqlalchemy/orm/strategies.py index f6b36f557e,ce19667c69..00b00be135 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@@ -576,98 -575,6 +576,81 @@@ class LoadLazyAttribute(object) else: return None +class SubqueryLoader(AbstractRelationshipLoader): + def init_class_attribute(self, mapper): + self.parent_property._get_strategy(LazyLoader).init_class_attribute(mapper) + + def setup_query(self, context, entity, path, adapter, + column_collection=None, parentmapper=None, **kwargs): + + if not context.query._enable_eagerloads: + return + + path = path + (self.key,) + - # TODO: shouldn't have to use getattr() to get at - # InstrumentedAttributes, or alternatively should not need to - # use InstrumentedAttributes with the Query at all (it should accept - # the MapperProperty objects as well). - + local_cols, remote_cols = self._local_remote_columns + - local_attr = [getattr(self.parent.class_, key) - for key in - [self.parent._get_col_to_prop(c).key for c in local_cols] - ] ++ local_attr = [ ++ self.parent._get_col_to_prop(c).class_attribute ++ for c in local_cols ++ ] + - attr = getattr(self.parent.class_, self.key) ++ attr = self.parent_property.class_attribute + + # modify the query to just look for parent columns in the join condition + - # TODO. secondary is not supported at all yet. - + # TODO: what happens to options() in the parent query ? are they going + # to get in the way here ? + + q = context.query._clone() + q._set_entities(local_attr) - if self.parent_property.secondary is not None: - q = q.from_self(self.mapper, *local_attr) - else: - q = q.from_self(self.mapper) ++ ++ q = q.from_self(self.mapper, *local_attr) ++ + q = q.join(attr) + - if self.parent_property.secondary is not None: - q = q.order_by(*local_attr) - else: - q = q.order_by(*remote_cols) ++ q = q.order_by(*local_attr) + + if self.parent_property.order_by: + q = q.order_by(*self.parent_property.order_by) + + context.attributes[('subquery', path)] = q + + @property + def _local_remote_columns(self): + if self.parent_property.secondary is None: + return zip(*self.parent_property.local_remote_pairs) + else: + return \ + [p[0] for p in self.parent_property.synchronize_pairs],\ + [p[0] for p in self.parent_property.secondary_synchronize_pairs] + + def create_row_processor(self, context, path, mapper, row, adapter): + path = path + (self.key,) + + local_cols, remote_cols = self._local_remote_columns + + local_attr = [self.parent._get_col_to_prop(c).key for c in local_cols] + remote_attr = [self.mapper._get_col_to_prop(c).key for c in remote_cols] + + q = context.attributes[('subquery', path)] + - if self.parent_property.secondary is not None: - collections = dict((k, [v[0] for v in v]) for k, v in itertools.groupby( - q, - lambda x:x[1:] - )) - else: - collections = dict((k, list(v)) for k, v in itertools.groupby( - q, - lambda x:tuple([getattr(x, key) for key in remote_attr]) - )) ++ collections = dict((k, [v[0] for v in v]) for k, v in itertools.groupby( ++ q, ++ lambda x:x[1:] ++ )) + + + def execute(state, dict_, row): + collection = collections.get( + tuple([row[col] for col in local_cols]), + () + ) + state.get_impl(self.key).set_committed_value(state, dict_, collection) + + return (execute, None) + + class EagerLoader(AbstractRelationshipLoader): """Strategize a relationship() that loads within the process of the parent object being selected."""