]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- the "polymorphic_primaryjoin" again goes against the parent's non-polymorphic local...
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 1 Feb 2007 03:18:08 +0000 (03:18 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 1 Feb 2007 03:18:08 +0000 (03:18 +0000)
lazy load clause evaluation is plenty solid enough to handle it this time.
- the join_to() method on PropertyLoader takes the parent mapper as an argument and alisiazes
the primaryjoin against that mapper's selectable, so that the same primary join can be used against
the base mapper, any inheriting mapper, etc., whether or not it uses a polymorphic union (although
needs to be tested against alternate polymorphic unions added on subclasses).  fixes [ticket:448]

CHANGES
lib/sqlalchemy/ext/selectresults.py
lib/sqlalchemy/orm/properties.py
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/orm/strategies.py
test/orm/inheritance5.py

diff --git a/CHANGES b/CHANGES
index 4ff29d3b008d5fac821f21c46be0d6687ea3a3ec..9b12671a5894d0fb7e5c03fc24fcd368be7f816d 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -9,7 +9,7 @@
   - further rework of the recent polymorphic relationship refactorings, as well
   as the mechanics of relationships overall.  Allows more accurate ORM behavior
   with relationships from/to/between polymorphic mappers, as well as their usage
-  with Query, SelectResults.  tickets include [ticket:439], [ticket:441].
+  with Query, SelectResults.  tickets include [ticket:439], [ticket:441], [ticket:448].
   relationship mechanics are still a work in progress, more to come !
   - eager relation to an inheriting mapper wont fail if no rows returned for
   the relationship.
index 23fd988930bbc662f18500c53f2cbbbd10cc24d4..3e7fa93c8705aa6838983c8d630a229016ac9da7 100644 (file)
@@ -134,9 +134,9 @@ class SelectResults(object):
         for key in keys:
             prop = mapper.props[key]
             if outerjoin:
-                clause = clause.outerjoin(prop.select_table, prop.get_join())
+                clause = clause.outerjoin(prop.select_table, prop.get_join(mapper))
             else:
-                clause = clause.join(prop.select_table, prop.get_join())
+                clause = clause.join(prop.select_table, prop.get_join(mapper))
             mapper = prop.mapper
         return (clause, mapper)
         
index 09c90c6acc948bc0e1497d85af51ff7da128d6c4..5e8fab807e5073df921d69d1870a6018224d648a 100644 (file)
@@ -87,6 +87,7 @@ class PropertyLoader(StrategizedProperty):
         self.collection_class = collection_class
         self.passive_deletes = passive_deletes
         self.remote_side = util.to_set(remote_side)
+        self._parent_join_cache = {}
         
         if cascade is not None:
             self.cascade = mapperutil.CascadeOptions(cascade)
@@ -260,7 +261,6 @@ class PropertyLoader(StrategizedProperty):
         # as we will be using the polymorphic selectables (i.e. select_table argument to Mapper) to figure this out, 
         # first create maps of all the "equivalent" columns, since polymorphic selectables will often munge
         # several "equivalent" columns (such as parent/child fk cols) into just one column.
-        parent_equivalents = self.parent._get_inherited_column_equivalents()
         target_equivalents = self.mapper._get_inherited_column_equivalents()
         
         # if the target mapper loads polymorphically, adapt the clauses to the target's selectable
@@ -281,15 +281,7 @@ class PropertyLoader(StrategizedProperty):
             self.polymorphic_primaryjoin = self.primaryjoin.copy_container()
             self.polymorphic_secondaryjoin = self.secondaryjoin and self.secondaryjoin.copy_container() or None
 
-        # if the parent mapper loads polymorphically, adapt the clauses to the parent's selectable
-        if self.parent.select_table is not self.parent.mapped_table:
-            if self.direction is sync.ONETOMANY:
-                self.polymorphic_primaryjoin.accept_visitor(sql_util.ClauseAdapter(self.parent.select_table, exclude=self.foreignkey, equivalents=parent_equivalents))
-            elif self.direction is sync.MANYTOONE:
-                self.polymorphic_primaryjoin.accept_visitor(sql_util.ClauseAdapter(self.parent.select_table, include=self.foreignkey, equivalents=parent_equivalents))
-            elif self.secondaryjoin:
-                self.polymorphic_primaryjoin.accept_visitor(sql_util.ClauseAdapter(self.parent.select_table, exclude=self.foreignkey, equivalents=parent_equivalents))
-
+            
         #print "KEY", self.key, "PARENT", str(self.parent)
         #print "KEY", self.key, "REG PRIMARY JOIN", str(self.primaryjoin)
         #print "KEY", self.key, "POLY PRIMARY JOIN", str(self.polymorphic_primaryjoin)
@@ -372,12 +364,30 @@ class PropertyLoader(StrategizedProperty):
             raise exceptions.ArgumentError("Cant determine relation direction for '%s' on mapper '%s' with primary join '%s' - no foreign key relationship is expressed within the join condition.  Specify 'foreignkey' argument." %(self.key, str(self.parent), str(self.primaryjoin)))
         self.foreignkey = foreignkeys
         
-    def get_join(self):
-        if self.polymorphic_secondaryjoin is not None:
-            return self.polymorphic_primaryjoin & self.polymorphic_secondaryjoin
-        else:
-            return self.polymorphic_primaryjoin
+    def get_join(self, parent):
+        try:
+            return self._parent_join_cache[parent]
+        except KeyError:
+            parent_equivalents = parent._get_inherited_column_equivalents()
+            primaryjoin = self.polymorphic_primaryjoin.copy_container()
+            if self.secondaryjoin is not None:
+                secondaryjoin = self.polymorphic_secondaryjoin.copy_container()
+            else:
+                secondaryjoin = None
+            if self.direction is sync.ONETOMANY:
+                primaryjoin.accept_visitor(sql_util.ClauseAdapter(parent.select_table, exclude=self.foreignkey, equivalents=parent_equivalents))
+            elif self.direction is sync.MANYTOONE:
+                primaryjoin.accept_visitor(sql_util.ClauseAdapter(parent.select_table, include=self.foreignkey, equivalents=parent_equivalents))
+            elif self.secondaryjoin:
+                primaryjoin.accept_visitor(sql_util.ClauseAdapter(parent.select_table, exclude=self.foreignkey, equivalents=parent_equivalents))
 
+            if secondaryjoin is not None:
+                j = primaryjoin & secondaryjoin
+            else:
+                j = primaryjoin
+            self._parent_join_cache[parent] = j
+            return j
+            
     def register_dependencies(self, uowcommit):
         if not self.viewonly:
             self._dependency_processor.register_dependencies(uowcommit)
index adec69116b600610c8189429d9fb0e06ce6c9d05..09863dbab581767a30c7c0e1fe9e8a7ab68e4548 100644 (file)
@@ -186,9 +186,9 @@ class Query(object):
         for key in keys:
             prop = mapper.props[key]
             if clause is None:
-                clause = prop.get_join()
+                clause = prop.get_join(mapper)
             else:
-                clause &= prop.get_join()
+                clause &= prop.get_join(mapper)
             mapper = prop.mapper
 
         return clause
index 29b60a8f614763e3b2901a15fc5a29bebd901adb..1cdf0b177ec73e1b35c13d557b5b8ef07bc51895 100644 (file)
@@ -156,7 +156,7 @@ class LazyLoader(AbstractRelationLoader):
     def init(self):
         super(LazyLoader, self).init()
         (self.lazywhere, self.lazybinds, self.lazyreverse) = self._create_lazy_clause(
-            self.parent.select_table, 
+            self.parent.local_table, 
             self.mapper.select_table,
             self.polymorphic_primaryjoin, 
             self.polymorphic_secondaryjoin, 
index 49eca2fc3c2c73a7baf64087fde69e2058590964..8a0ec165f9b861df5a4b104fe89bd4b550996e0a 100644 (file)
@@ -546,14 +546,13 @@ class SelectResultsTest(testbase.AssertMixin):
         session.save(car2)
         session.flush()
 
-#        for activeCars in SelectResults(session.query(Car)).join_to('status').select(status.c.name=="active"):
-#            print activeCars
-        for activePerson in  SelectResults(session.query(Person)).join_to('status').select(status.c.name=="active"):
-            print activePerson
-#        for activePerson in  SelectResults(session.query(Person)).join_to('status').select_by(name="active"):
-#            print activePerson
-
-
+        # test these twice because theres caching involved
+        for x in range(0, 2):
+            r = SelectResults(session.query(Person)).select_by(people.c.name.like('%2')).join_to('status').select_by(name="active")
+            assert str(list(r)) == "[Manager M2, category YYYYYYYYY, status Status active, Engineer E2, field X, status Status active]"
+            r = SelectResults(session.query(Engineer)).join_to('status').select(people.c.name.in_('E2', 'E3', 'E4', 'M4', 'M2', 'M1') & (status.c.name=="active"))
+            assert str(list(r)) == "[Engineer E2, field X, status Status active, Engineer E3, field X, status Status active]"
+        
 class MultiLevelTest(testbase.ORMTest):
     def define_tables(self, metadata):
         global table_Employee, table_Engineer, table_Manager