]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- added a feature to eager loading whereby subqueries set
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 30 Apr 2008 01:16:05 +0000 (01:16 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 30 Apr 2008 01:16:05 +0000 (01:16 +0000)
as column_property() with explicit label names (which is not
necessary, btw) will have the label anonymized when
the instance is part of the eager join, to prevent
conflicts with a subquery or column of the same name
on the parent object.  [ticket:1019]

CHANGES
lib/sqlalchemy/orm/util.py
test/orm/eager_relations.py

diff --git a/CHANGES b/CHANGES
index 102aa725db62b49786a9c47b2f0cf9ee699bbdcb..c3f84386d4331338d22f85461b303926988f6228 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -39,6 +39,13 @@ CHANGES
     - refined mapper._save_obj() which was unnecessarily calling
       __ne__() on scalar values during flush [ticket:1015]
       
+    - added a feature to eager loading whereby subqueries set
+      as column_property() with explicit label names (which is not
+      necessary, btw) will have the label anonymized when
+      the instance is part of the eager join, to prevent
+      conflicts with a subquery or column of the same name 
+      on the parent object.  [ticket:1019]
+      
 - sql
     - Added COLLATE support via the .collate(<collation>)
       expression operator and collate(<expr>, <collation>) sql
index 0a507dcf7f590b3972baefa800c0911f8bc6662a..19e5e59b93280f213d021dfc47b63b8552fb26bf 100644 (file)
@@ -166,6 +166,10 @@ class AliasedClauses(object):
         # process column-level subqueries    
         aliased_column = sql_util.ClauseAdapter(self.alias, equivalents=self.equivalents).traverse(column, clone=True)
 
+        # anonymize labels which might have specific names
+        if isinstance(aliased_column, expression._Label):
+            aliased_column = aliased_column.label(None)
+
         # add to row decorator explicitly
         self.row_decorator({}).map[column] = aliased_column
         return aliased_column
index acc51937eebb948613d18750028c4ef802d7e9f6..a30c425ac5c5edb3c68ffd79cbf3e1fff786674f 100644 (file)
@@ -975,5 +975,75 @@ class CyclicalInheritingEagerTest(ORMTest):
         # testing a particular endless loop condition in eager join setup
         create_session().query(SubT).all()
 
+class SubqueryTest(ORMTest):
+    def define_tables(self, metadata):
+        global users_table, tags_table
+        
+        users_table = Table('users', metadata, 
+            Column('id', Integer, primary_key=True),
+            Column('name', String(16))
+        )
+
+        tags_table = Table('tags', metadata,
+            Column('id', Integer, primary_key=True),
+            Column('user_id', Integer, ForeignKey("users.id")),
+            Column('score1', Float),
+            Column('score2', Float),
+        )
+
+    def test_label_anonymizing(self):
+        """test that eager loading works with subqueries with labels, 
+        even if an explicit labelname which conflicts with a label on the parent.
+        
+        There's not much reason a column_property() would ever need to have a label
+        of a specific name (and they don't even need labels these days), 
+        unless you'd like the name to line up with a name
+        that you may be using for a straight textual statement used for loading
+        instances of that type.
+        
+        """
+        class User(Base):
+            @property
+            def prop_score(self):
+                return sum(tag.prop_score for tag in self.tags)
+
+        class Tag(Base):
+            @property
+            def prop_score(self):
+                return self.score1 * self.score2
+        
+        for labeled, labelname in [(True, 'score'), (True, None), (False, None)]:
+            clear_mappers()
+            
+            tag_score = (tags_table.c.score1 * tags_table.c.score2)
+            user_score = select([func.sum(tags_table.c.score1 *
+                                          tags_table.c.score2)],
+                                tags_table.c.user_id == users_table.c.id)
+            
+            if labeled:
+                tag_score = tag_score.label(labelname)
+                user_score = user_score.label(labelname)
+            else:
+                user_score = user_score.as_scalar()
+            
+            mapper(Tag, tags_table, properties={
+                'query_score': column_property(tag_score),
+            })
+
+
+            mapper(User, users_table, properties={
+                'tags': relation(Tag, backref='user', lazy=False), 
+                'query_score': column_property(user_score),
+            })
+
+            session = create_session()
+            session.save(User(name='joe', tags=[Tag(score1=5.0, score2=3.0), Tag(score1=55.0, score2=1.0)]))
+            session.save(User(name='bar', tags=[Tag(score1=5.0, score2=4.0), Tag(score1=50.0, score2=1.0), Tag(score1=15.0, score2=2.0)]))
+            session.flush()
+            session.clear()
+
+            for user in session.query(User).all():
+                self.assertEquals(user.query_score, user.prop_score)
+
 if __name__ == '__main__':
     testenv.main()