]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- move properties to use the new create_joins
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 10 Feb 2012 22:50:15 +0000 (17:50 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 10 Feb 2012 22:50:15 +0000 (17:50 -0500)
- fix up subquery eager loading

lib/sqlalchemy/orm/properties.py
lib/sqlalchemy/orm/relationships.py
lib/sqlalchemy/orm/strategies.py
test/orm/test_rel_fn.py

index 38237b2d4b213fdd270f0ccfb5705246e01df4bd..f7a979d0e3b3829aed5f77da455a951b22582f0f 100644 (file)
@@ -920,6 +920,61 @@ class RelationshipProperty(StrategizedProperty):
         self._generate_backref()
         super(RelationshipProperty, self).do_init()
 
+    def _process_dependent_arguments(self):
+        """Convert incoming configuration arguments to their 
+        proper form.
+
+        Callables are resolved, ORM annotations removed.
+
+        """
+        # accept callables for other attributes which may require
+        # deferred initialization.  This technique is used
+        # by declarative "string configs" and some recipes.
+        for attr in (
+            'order_by', 'primaryjoin', 'secondaryjoin',
+            'secondary', '_user_defined_foreign_keys', 'remote_side',
+            ):
+            attr_value = getattr(self, attr)
+            if util.callable(attr_value):
+                setattr(self, attr, attr_value())
+
+        # remove "annotations" which are present if mapped class
+        # descriptors are used to create the join expression.
+        for attr in 'primaryjoin', 'secondaryjoin':
+            val = getattr(self, attr)
+            if val is not None:
+                setattr(self, attr, _orm_deannotate(
+                    expression._only_column_elements(val, attr))
+                )
+
+        # ensure expressions in self.order_by, foreign_keys,
+        # remote_side are all columns, not strings.
+        if self.order_by is not False and self.order_by is not None:
+            self.order_by = [
+                    expression._only_column_elements(x, "order_by") 
+                    for x in
+                    util.to_list(self.order_by)]
+
+        self._user_defined_foreign_keys = \
+            util.column_set(
+                    expression._only_column_elements(x, "foreign_keys") 
+                    for x in util.to_column_set(
+                        self._user_defined_foreign_keys
+                    ))
+
+        self.remote_side = \
+            util.column_set(
+                    expression._only_column_elements(x, "remote_side") 
+                    for x in
+                    util.to_column_set(self.remote_side))
+
+        self.target = self.mapper.mapped_table
+
+        if self.cascade.delete_orphan:
+            self.mapper.primary_mapper().delete_orphans.append(
+                            (self.key, self.parent.class_)
+                        )
+
     def _setup_join_conditions(self):
         self._join_condition = jc = relationships.JoinCondition(
                     parent_selectable=self.parent.mapped_table,
@@ -944,6 +999,7 @@ class RelationshipProperty(StrategizedProperty):
         self.direction = jc.direction
         self.local_remote_pairs = jc.local_remote_pairs
         self.remote_side = jc.remote_columns
+        self.local_columns = jc.local_columns
         self.synchronize_pairs = jc.synchronize_pairs
         self._calculated_foreign_keys = jc.foreign_key_columns
         self.secondary_synchronize_pairs = jc.secondary_synchronize_pairs
@@ -975,64 +1031,6 @@ class RelationshipProperty(StrategizedProperty):
                               "cause dependency issues during flush"
                               % (self.key, self.parent, inheriting))
 
-    def _process_dependent_arguments(self):
-        """Convert incoming configuration arguments to their 
-        proper form.
-
-        Callables are resolved, ORM annotations removed.
-
-        """
-        # accept callables for other attributes which may require
-        # deferred initialization.  This technique is used
-        # by declarative "string configs" and some recipes.
-        for attr in (
-            'order_by',
-            'primaryjoin',
-            'secondaryjoin',
-            'secondary',
-            '_user_defined_foreign_keys',
-            'remote_side',
-            ):
-            attr_value = getattr(self, attr)
-            if util.callable(attr_value):
-                setattr(self, attr, attr_value())
-
-        # remove "annotations" which are present if mapped class
-        # descriptors are used to create the join expression.
-        for attr in 'primaryjoin', 'secondaryjoin':
-            val = getattr(self, attr)
-            if val is not None:
-                setattr(self, attr, _orm_deannotate(
-                    expression._only_column_elements(val, attr))
-                )
-
-        # ensure expressions in self.order_by, foreign_keys,
-        # remote_side are all columns, not strings.
-        if self.order_by is not False and self.order_by is not None:
-            self.order_by = [
-                    expression._only_column_elements(x, "order_by") 
-                    for x in
-                    util.to_list(self.order_by)]
-
-        self._user_defined_foreign_keys = \
-            util.column_set(
-                    expression._only_column_elements(x, "foreign_keys") 
-                    for x in util.to_column_set(
-                        self._user_defined_foreign_keys
-                    ))
-
-        self.remote_side = \
-            util.column_set(
-                    expression._only_column_elements(x, "remote_side") 
-                    for x in
-                    util.to_column_set(self.remote_side))
-
-        self.target = self.mapper.mapped_table
-
-        if self.cascade.delete_orphan:
-            self.mapper.primary_mapper().delete_orphans.append(
-                            (self.key, self.parent.class_)
-                        )
 
     def _check_cascade_settings(self):
         if self.cascade.delete_orphan and not self.single_parent \
@@ -1098,14 +1096,11 @@ class RelationshipProperty(StrategizedProperty):
             kwargs.setdefault('passive_updates', self.passive_updates)
             self.back_populates = backref_key
             relationship = RelationshipProperty(
-                parent,
-                self.secondary,
-                pj,
-                sj,
+                parent, self.secondary,
+                pj, sj,
                 foreign_keys=foreign_keys,
                 back_populates=self.key,
-                **kwargs
-                )
+                **kwargs)
             mapper._configure_property(backref_key, relationship)
 
         if self.back_populates:
@@ -1155,78 +1150,22 @@ class RelationshipProperty(StrategizedProperty):
         else:
             aliased = True
 
-        # place a barrier on the destination such that
-        # replacement traversals won't ever dig into it.
-        # its internal structure remains fixed 
-        # regardless of context.
-        dest_selectable = _shallow_annotate(
-                                dest_selectable, 
-                                {'no_replacement_traverse':True})
-
-        aliased = aliased or (source_selectable is not None)
-
-        primaryjoin, secondaryjoin, secondary = self.primaryjoin, \
-            self.secondaryjoin, self.secondary
-
-        # adjust the join condition for single table inheritance,
-        # in the case that the join is to a subclass
-        # this is analogous to the "_adjust_for_single_table_inheritance()"
-        # method in Query.
-
         dest_mapper = of_type or self.mapper
 
         single_crit = dest_mapper._single_table_criterion
-        if single_crit is not None:
-            if secondaryjoin is not None:
-                secondaryjoin = secondaryjoin & single_crit
-            else:
-                primaryjoin = primaryjoin & single_crit
-
-        if aliased:
-            if secondary is not None:
-                secondary = secondary.alias()
-                primary_aliasizer = ClauseAdapter(secondary)
-                secondary_aliasizer = \
-                    ClauseAdapter(dest_selectable,
-                        equivalents=self.mapper._equivalent_columns).\
-                        chain(primary_aliasizer)
-                if source_selectable is not None:
-                    primary_aliasizer = \
-                        ClauseAdapter(secondary).\
-                            chain(ClauseAdapter(source_selectable,
-                            equivalents=self.parent._equivalent_columns))
-                secondaryjoin = \
-                    secondary_aliasizer.traverse(secondaryjoin)
-            else:
-                primary_aliasizer = ClauseAdapter(dest_selectable,
-                        #exclude=self.local_side,
-                        exclude_fn=lambda c: "local" in c._annotations,
-                        equivalents=self.mapper._equivalent_columns)
-                if source_selectable is not None:
-                    primary_aliasizer.chain(
-                        ClauseAdapter(source_selectable,
-                            #exclude=self.remote_side,
-                            exclude_fn=lambda c: "remote" in c._annotations,
-                            equivalents=self.parent._equivalent_columns))
-                secondary_aliasizer = None
-
-            primaryjoin = primary_aliasizer.traverse(primaryjoin)
-            target_adapter = secondary_aliasizer or primary_aliasizer
-            target_adapter.include = target_adapter.exclude = target_adapter.exclude_fn = None
-        else:
-            target_adapter = None
+        aliased = aliased or (source_selectable is not None)
+
+        primaryjoin, secondaryjoin, secondary, target_adapter, dest_selectable = \
+            self._join_condition.join_targets(
+                source_selectable, dest_selectable, aliased, single_crit
+            )
         if source_selectable is None:
             source_selectable = self.parent.local_table
         if dest_selectable is None:
             dest_selectable = self.mapper.local_table
-        return (
-            primaryjoin,
-            secondaryjoin,
-            source_selectable,
-            dest_selectable,
-            secondary,
-            target_adapter,
-            )
+        return (primaryjoin, secondaryjoin, source_selectable,
+            dest_selectable, secondary, target_adapter)
+
 
 PropertyLoader = RelationProperty = RelationshipProperty
 log.class_logger(RelationshipProperty)
index 515ca773ba6646331569b66303295c5c86be6e96..413397fda1707e06418b04009c20b171276f89d4 100644 (file)
@@ -599,7 +599,8 @@ class JoinCondition(object):
             target_adapter.exclude_fn = None
         else:
             target_adapter = None
-        return primaryjoin, secondaryjoin, secondary, target_adapter
+        return primaryjoin, secondaryjoin, secondary, target_adapter, dest_selectable
+
 
 ################# everything below is TODO ################################
 
index 32023428190488a7f6d0d586ed79855a84d7bcdc..00739ea03bde0f39c3caa640e28827fd0ae6c917 100644 (file)
@@ -785,8 +785,8 @@ class SubqueryLoader(AbstractRelationshipLoader):
             leftmost_mapper, leftmost_prop = \
                                     subq_mapper, \
                                     subq_mapper._props[subq_path[1]]
-        # TODO: local cols might not be unique here
-        leftmost_cols, remote_cols = self._local_remote_columns(leftmost_prop)
+
+        leftmost_cols = leftmost_prop.local_columns
 
         leftmost_attr = [
             leftmost_mapper._columntoproperty[c].class_attribute
@@ -847,9 +847,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
             # self.parent is more specific than subq_path[-2]
             parent_alias = mapperutil.AliasedClass(self.parent)
 
-        # TODO: local cols might not be unique here
-        local_cols, remote_cols = \
-                        self._local_remote_columns(self.parent_property)
+        local_cols = self.parent_property.local_columns
 
         local_attr = [
             getattr(parent_alias, self.parent._columntoproperty[c].key)
@@ -883,18 +881,6 @@ class SubqueryLoader(AbstractRelationshipLoader):
                 q = q.join(attr, aliased=middle, from_joinpoint=True)
         return q
 
-    def _local_remote_columns(self, prop):
-        if prop.secondary is None:
-            return zip(*prop.local_remote_pairs)
-        else:
-            # TODO: this isn't going to work for readonly....
-            return \
-                [p[0] for p in prop.synchronize_pairs],\
-                [
-                    p[0] for p in prop.
-                                        secondary_synchronize_pairs
-                ]
-
     def _setup_options(self, q, subq_path, orig_query):
         # propagate loader options etc. to the new query.
         # these will fire relative to subq_path.
@@ -933,8 +919,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
         if ('subquery', reduced_path) not in context.attributes:
             return None, None, None
 
-        # TODO: local_cols might not be unique here
-        local_cols, remote_cols = self._local_remote_columns(self.parent_property)
+        local_cols = self.parent_property.local_columns
 
         q = context.attributes[('subquery', reduced_path)]
 
index 21fc681917e271268b7328bf39e2fbb8ce142739..56cba3c44c8e12e461dbb67eb06f3c410800dc97 100644 (file)
@@ -400,7 +400,7 @@ class AdaptedJoinTest(_JoinFixtures, fixtures.TestBase, AssertsCompiledSQL):
     def test_join_targets_o2m_selfref(self):
         joincond = self._join_fixture_o2m_selfref()
         left = select([joincond.parent_selectable]).alias('pj')
-        pj, sj, sec, adapter = joincond.join_targets(
+        pj, sj, sec, adapter, ds = joincond.join_targets(
                                     left, 
                                     joincond.child_selectable, 
                                     True)
@@ -409,7 +409,7 @@ class AdaptedJoinTest(_JoinFixtures, fixtures.TestBase, AssertsCompiledSQL):
         )
 
         right = select([joincond.child_selectable]).alias('pj')
-        pj, sj, sec, adapter = joincond.join_targets(
+        pj, sj, sec, adapter, ds = joincond.join_targets(
                                     joincond.parent_selectable, 
                                     right, 
                                     True)
@@ -420,7 +420,7 @@ class AdaptedJoinTest(_JoinFixtures, fixtures.TestBase, AssertsCompiledSQL):
 
     def test_join_targets_o2m_plain(self):
         joincond = self._join_fixture_o2m()
-        pj, sj, sec, adapter = joincond.join_targets(
+        pj, sj, sec, adapter, ds = joincond.join_targets(
                                     joincond.parent_selectable, 
                                     joincond.child_selectable, 
                                     False)
@@ -431,7 +431,7 @@ class AdaptedJoinTest(_JoinFixtures, fixtures.TestBase, AssertsCompiledSQL):
     def test_join_targets_o2m_left_aliased(self):
         joincond = self._join_fixture_o2m()
         left = select([joincond.parent_selectable]).alias('pj')
-        pj, sj, sec, adapter = joincond.join_targets(
+        pj, sj, sec, adapter, ds = joincond.join_targets(
                                     left, 
                                     joincond.child_selectable, 
                                     True)
@@ -442,7 +442,7 @@ class AdaptedJoinTest(_JoinFixtures, fixtures.TestBase, AssertsCompiledSQL):
     def test_join_targets_o2m_right_aliased(self):
         joincond = self._join_fixture_o2m()
         right = select([joincond.child_selectable]).alias('pj')
-        pj, sj, sec, adapter = joincond.join_targets(
+        pj, sj, sec, adapter, ds = joincond.join_targets(
                                     joincond.parent_selectable, 
                                     right, 
                                     True)