]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- annotations store 'parententity' as well as 'parentmapper'
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 13 Feb 2009 18:08:40 +0000 (18:08 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 13 Feb 2009 18:08:40 +0000 (18:08 +0000)
- ORMAdapter filters all replacements against a non-compatible 'parentmapper' annotation
- Other filterings, like
query(A).join(A.bs).filter(B.foo=='bar'), were erroneously
adapting "B.foo" as though it were an "A".

CHANGES
lib/sqlalchemy/orm/evaluator.py
lib/sqlalchemy/orm/properties.py
lib/sqlalchemy/orm/util.py
test/orm/inheritance/query.py

diff --git a/CHANGES b/CHANGES
index 9c4ab7d73da15e25c91f602ca0a207023877da70..1603ce65946e8552d447141a4cf0a3365995e951 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -34,10 +34,18 @@ CHANGES
       
     - Fixed bugs in Query regarding simultaneous selection of 
       multiple joined-table inheritance entities with common base 
-      classes, previously the adaption applied to "e2" on 
-      "e1 JOIN e2" would be partially applied to "e1".  Additionally,
-      comparisons on relations (i.e. Entity2.related==e2)
-      were not getting adapted correctly.
+      classes:
+      
+      - previously the adaption applied to "B" on 
+        "A JOIN B" would be erroneously partially applied 
+        to "A".
+      
+      - comparisons on relations (i.e. A.related==someb)
+        were not getting adapted when they should.
+      
+      - Other filterings, like 
+        query(A).join(A.bs).filter(B.foo=='bar'), were erroneously
+        adapting "B.foo" as though it were an "A".
       
 - sql
     - Fixed missing _label attribute on Function object, others
index 03955afa8a50dbce9718c59f28aa9936e5703e98..4611dd91b46c5951306511f7bd9041ca2bd18c3e 100644 (file)
@@ -30,8 +30,8 @@ class EvaluatorCompiler(object):
         return lambda obj: None
 
     def visit_column(self, clause):
-        if 'parententity' in clause._annotations:
-            key = clause._annotations['parententity']._get_col_to_prop(clause).key
+        if 'parentmapper' in clause._annotations:
+            key = clause._annotations['parentmapper']._get_col_to_prop(clause).key
         else:
             key = clause.key
         get_corresponding_attr = operator.attrgetter(key)
index 42c3bffa3bb0aad65a8b2928936dbd6c8909b517..2a772dcac244a7d4a2b78da93b05fb8f21fdbea7 100644 (file)
@@ -121,7 +121,7 @@ class ColumnProperty(StrategizedProperty):
             if self.adapter:
                 return self.adapter(self.prop.columns[0])
             else:
-                return self.prop.columns[0]._annotate({"parententity": self.mapper})
+                return self.prop.columns[0]._annotate({"parententity": self.mapper, "parentmapper":self.mapper})
                 
         def operate(self, op, *other, **kwargs):
             return op(self.__clause_element__(), *other, **kwargs)
index c8637290177ef598bc320cf0a5b843f1f4017e76..1cca9e00b863602f2463ef308586e5a513262322 100644 (file)
@@ -258,13 +258,20 @@ class ORMAdapter(sql_util.ColumnAdapter):
     
     """
     def __init__(self, entity, equivalents=None, chain_to=None):
-        mapper, selectable, is_aliased_class = _entity_info(entity)
+        self.mapper, selectable, is_aliased_class = _entity_info(entity)
         if is_aliased_class:
             self.aliased_class = entity
         else:
             self.aliased_class = None
         sql_util.ColumnAdapter.__init__(self, selectable, equivalents, chain_to)
 
+    def replace(self, elem):
+        entity = elem._annotations.get('parentmapper', None)
+        if not entity or entity.isa(self.mapper):
+            return sql_util.ColumnAdapter.replace(self, elem)
+        else:
+            return None
+
 class AliasedClass(object):
     """Represents an 'alias'ed form of a mapped class for usage with Query.
     
@@ -303,7 +310,7 @@ class AliasedClass(object):
         self.__name__ = 'AliasedClass_' + str(self.__target)
         
     def __adapt_element(self, elem):
-        return self.__adapter.traverse(elem)._annotate({'parententity': self})
+        return self.__adapter.traverse(elem)._annotate({'parententity': self, 'parentmapper':self.__mapper})
         
     def __adapt_prop(self, prop):
         existing = getattr(self.__target, prop.key)
index 07bf068b72ac62fcdddd8b2381d52de64d8b65d9..ca789f8338ea23237bfc595833bca29a64f201aa 100644 (file)
@@ -750,7 +750,7 @@ class SelfReferentialTestJoinedToBase(ORMTest):
             sess.query(Engineer).join('reports_to', aliased=True).filter(Person.name=='dogbert').first(), 
             Engineer(name='dilbert'))
 
-class SelfReferentialTestJoinedToJoined(ORMTest):
+class SelfReferentialJ2JTest(ORMTest):
     keep_mappers = True
 
     def define_tables(self, metadata):
@@ -803,6 +803,36 @@ class SelfReferentialTestJoinedToJoined(ORMTest):
             sess.query(Engineer).join('reports_to', aliased=True).filter(Manager.name=='dogbert').first(), 
             Engineer(name='dilbert'))
     
+    def test_filter_aliasing(self):
+        m1 = Manager(name='dogbert')
+        m2 = Manager(name='foo')
+        e1 = Engineer(name='wally', primary_language='java', reports_to=m1)
+        e2 = Engineer(name='dilbert', primary_language='c++', reports_to=m2)
+        e3 = Engineer(name='etc', primary_language='c++')
+        sess = create_session()
+        sess.add_all([m1, m2, e1, e2, e3])
+        sess.flush()
+        sess.expunge_all()
+
+        # filter aliasing applied to Engineer doesn't whack Manager
+        self.assertEquals(
+            sess.query(Manager).join(Manager.engineers).filter(Manager.name=='dogbert').all(),
+            [m1]
+        )
+
+        self.assertEquals(
+            sess.query(Manager).join(Manager.engineers).filter(Engineer.name=='dilbert').all(),
+            [m2]
+        )
+
+        self.assertEquals(
+            sess.query(Manager, Engineer).join(Manager.engineers).order_by(Manager.name.desc()).all(),
+            [
+                (m2, e2),
+                (m1, e1),
+            ]
+        )
+        
     def test_relation_compare(self):
         m1 = Manager(name='dogbert')
         m2 = Manager(name='foo')
@@ -827,8 +857,7 @@ class SelfReferentialTestJoinedToJoined(ORMTest):
             sess.query(Manager).join(Manager.engineers).filter(Engineer.reports_to==m1).all(), 
             [m1]
         )
-        
-        
+
         
 
 class M2MFilterTest(ORMTest):