else:
return None
- # 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).
-
+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,)
+
- local_attr = [getattr(self.parent.class_, key)
- for key in
- [self.parent._get_col_to_prop(c).key for c in local_cols]
- ]
+ local_cols, remote_cols = self._local_remote_columns
+
- attr = getattr(self.parent.class_, self.key)
++ local_attr = [
++ self.parent._get_col_to_prop(c).class_attribute
++ for c in local_cols
++ ]
+
- # TODO. secondary is not supported at all yet.
-
++ attr = self.parent_property.class_attribute
+
+ # modify the query to just look for parent columns in the join condition
+
- if self.parent_property.secondary is not None:
- q = q.from_self(self.mapper, *local_attr)
- else:
- q = q.from_self(self.mapper)
+ # 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.order_by(*local_attr)
- else:
- q = q.order_by(*remote_cols)
++
++ q = q.from_self(self.mapper, *local_attr)
++
+ q = q.join(attr)
+
- 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])
- ))
++ 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)]
+
++ 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."""