]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- added "alias" argument to contains_eager(). use it to specify the string name
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 20 Feb 2007 01:04:07 +0000 (01:04 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 20 Feb 2007 01:04:07 +0000 (01:04 +0000)
    or Alias instance of an alias used in the query for the eagerly loaded child items.
    easier to use than "decorator"

CHANGES
doc/build/content/adv_datamapping.txt
lib/sqlalchemy/orm/__init__.py
lib/sqlalchemy/orm/strategies.py
test/orm/mapper.py

diff --git a/CHANGES b/CHANGES
index 5a2ee05606429637ed38fd06edc9075f7657ceb7..494cc8d3e7a42d6be7a67fba41dc844cffcd3b52 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -33,6 +33,9 @@
   - improved support for complex queries embedded into "where" criterion
   for query.select() [ticket:449]
   - contains_eager('foo') automatically implies eagerload('foo')
+  - added "alias" argument to contains_eager().  use it to specify the string name
+    or Alias instance of an alias used in the query for the eagerly loaded child items.
+    easier to use than "decorator"
   - mapper options like eagerload(), lazyload(), deferred(), will work for "synonym()"
   relationships [ticket:485]
   - fixed bug where cascade operations incorrectly included deleted collection
index e1d6d92aa9591e3e0be7528bd6934bc127549362..cf49dc341059ee483b22032898fcac4dbf45788d 100644 (file)
@@ -770,7 +770,7 @@ When result-set mapping is used with a particular Mapper, SQLAlchemy has no acce
     # get results normally
     r = query.instances(statement.execute())
 
-It is often the case with large queries that some of the tables within the query need to be aliased in order to distinguish them from other occurences of the same table within the query.  A query that attempts to add eagerly loaded child items will often have this condition.  Therefore with a little more effort a decorator function can be used to produce translated rows (in the form of a dictionary which accepts Column instances), in the case that aliasing is used for the relationship tables.
+It is often the case with large queries that some of the tables within the query need to be aliased in order to distinguish them from other occurences of the same table within the query.  A query that attempts to add eagerly loaded child items will often have this condition.  The `contains_eager()` function takes a keyword argument `alias` which can either be the string name of an alias, or an actual `Alias` construct used in constructing the query, which will target the eager loading towards the columns of that alias (new in version 0.3.5):
 
     {python}
     # use an alias of the addresses table
@@ -779,17 +779,8 @@ It is often the case with large queries that some of the tables within the query
     # define a query on USERS with an outer join to adalias
     statement = users_table.outerjoin(adalias).select(use_labels=True)
 
-    # define row-translation function.  this should return 
-    # a dictionary-like object that will receive Column instances from the normally expected
-    # table (i.e. addreses_table), and produce results from the actual result set
-    def adtranslator(row):
-        d = {}
-        for c in addresses_table.columns:
-            d[c] = row[adalias.corresponding_column(addresses_table)]
-        return d
-        
     # construct a Query object which expects the "addresses" results 
-    query = session.query(User).options(contains_eager('addresses', decorator=adtranslator))
+    query = session.query(User).options(contains_eager('addresses', alias=adalias))
 
     # get results normally
     {sql}r = query.instances(statement.execute())
index 222383085dac7e4ba37a677349a0a5270f69cc87..ac9272a173125491d83a8cdef1561bcf6ff35fff 100644 (file)
@@ -119,14 +119,21 @@ def noload(name):
     used with query.options()."""
     return strategies.EagerLazyOption(name, lazy=None)
 
-def contains_eager(key, decorator=None):
+def contains_eager(key, alias=None, decorator=None):
     """return a MapperOption that will indicate to the query that the given 
-    attribute will be eagerly loaded without any row decoration, or using
-    a custom row decorator.  
-    
+    attribute will be eagerly loaded.
+
     used when feeding SQL result sets directly into
-    query.instances().  Also bundles an EagerLazyOption to turn on eager loading in case it isnt already."""
-    return (strategies.EagerLazyOption(key, lazy=False), strategies.RowDecorateOption(key, decorator=decorator))
+    query.instances().  Also bundles an EagerLazyOption to turn on eager loading 
+    in case it isnt already.
+        
+    "alias" is the string name of an alias, *or* an sql.Alias object, which represents
+    the aliased columns in the query.  this argument is optional.
+    
+    "decorator" is mutually exclusive of "alias" and is a row-processing function which
+    will be applied to the incoming row before sending to the eager load handler.  use this
+    for more sophisticated row adjustments beyond a straight alias."""
+    return (strategies.EagerLazyOption(key, lazy=False), strategies.RowDecorateOption(key, alias=alias, decorator=decorator))
     
 def defer(name):
     """return a MapperOption that will convert the column property of the given 
index 390b83867b2fb43459eb999158b90f37989c3b2c..0ebe397adcb7bfe5640b895263df6bf8406749a6 100644 (file)
@@ -576,10 +576,20 @@ class EagerLazyOption(StrategizedOption):
 EagerLazyOption.logger = logging.class_logger(EagerLazyOption)
 
 class RowDecorateOption(PropertyOption):
-    def __init__(self, key, decorator=None):
+    def __init__(self, key, decorator=None, alias=None):
         super(RowDecorateOption, self).__init__(key)
         self.decorator = decorator
+        self.alias = alias
     def process_selection_property(self, context, property):
+        if self.alias is not None and self.decorator is None:
+            if isinstance(self.alias, basestring):
+                self.alias = property.target.alias(self.alias)
+            def decorate(row):
+                d = {}
+                for c in property.target.columns:
+                    d[c] = row[self.alias.corresponding_column(c)]
+                return d
+            self.decorator = decorate
         context.attributes[(EagerLoader, property)] = self.decorator
 RowDecorateOption.logger = logging.class_logger(RowDecorateOption)
         
index aa3ca04773d9952289d01f3771ee7656f5840114..77c90863a2ef82f35e6390b93b19fda802acafbb 100644 (file)
@@ -1068,6 +1068,36 @@ class EagerTest(MapperSuperTest):
             self.assert_result(l, User, *user_address_result)
         self.assert_sql_count(testbase.db, go, 1)
 
+    def testcustomeagerwithstringalias(self):
+        mapper(User, users, properties={
+            'addresses':relation(Address, lazy=False)
+        })
+        mapper(Address, addresses)
+
+        adalias = addresses.alias('adalias')
+        selectquery = users.outerjoin(adalias).select(use_labels=True)
+        q = create_session().query(User)
+
+        def go():
+            l = q.options(contains_eager('addresses', alias="adalias")).instances(selectquery.execute())
+            self.assert_result(l, User, *user_address_result)
+        self.assert_sql_count(testbase.db, go, 1)
+
+    def testcustomeagerwithalias(self):
+        mapper(User, users, properties={
+            'addresses':relation(Address, lazy=False)
+        })
+        mapper(Address, addresses)
+
+        adalias = addresses.alias('adalias')
+        selectquery = users.outerjoin(adalias).select(use_labels=True)
+        q = create_session().query(User)
+
+        def go():
+            l = q.options(contains_eager('addresses', alias=adalias)).instances(selectquery.execute())
+            self.assert_result(l, User, *user_address_result)
+        self.assert_sql_count(testbase.db, go, 1)
+
     def testcustomeagerwithdecorator(self):
         mapper(User, users, properties={
             'addresses':relation(Address, lazy=False)