]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- added "contains_alias()" option for result set mapping to an alias of the mapped...
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 20 Feb 2007 02:12:46 +0000 (02:12 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 20 Feb 2007 02:12:46 +0000 (02:12 +0000)
CHANGES
doc/build/content/adv_datamapping.txt
lib/sqlalchemy/orm/__init__.py
lib/sqlalchemy/orm/mapper.py
test/orm/mapper.py

diff --git a/CHANGES b/CHANGES
index 494cc8d3e7a42d6be7a67fba41dc844cffcd3b52..f9db501f45e639e1289c17f37fba25d7966105f8 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -36,6 +36,7 @@
   - 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"
+  - added "contains_alias()" option for result set mapping to an alias of the mapped table
   - mapper options like eagerload(), lazyload(), deferred(), will work for "synonym()"
   relationships [ticket:485]
   - fixed bug where cascade operations incorrectly included deleted collection
index cf49dc341059ee483b22032898fcac4dbf45788d..d6102b424afcc1a9e8ea9111c041af9212abebd5 100644 (file)
@@ -788,6 +788,23 @@ It is often the case with large queries that some of the tables within the query
     adalias.user_id AS adalias_user_id, adalias.email_address AS adalias_email_address, (...other columns...)
     FROM users LEFT OUTER JOIN email_addresses AS adalias ON users.user_id = adalias.user_id
 
+In the case that the main table itself is also aliased, the `contains_alias()` option can be used (new in version 0.3.5):
+
+    {python}
+    # define an aliased UNION called 'ulist'
+    statement = users.select(users.c.user_id==7).union(users.select(users.c.user_id>7)).alias('ulist')
+
+    # add on an eager load of "addresses"
+    statement = statement.outerjoin(addresses).select(use_labels=True)
+    
+    # create query, indicating "ulist" is an alias for the main table, "addresses" property should
+    # be eager loaded
+    query = create_session().query(User).options(contains_alias('ulist'), contains_eager('addresses'))
+    
+    # results
+    r = query.instances(statement.execute())
+    
+
 ### Mapper Options {@name=mapperoptions}
 
 Options which can be sent to the `mapper()` function.  For arguments to `relation()`, see [advdatamapping_properties_relationoptions](rel:advdatamapping_properties_relationoptions).
index ac9272a173125491d83a8cdef1561bcf6ff35fff..30070e8d72602bd6c903d5863ae52cbf00718d28 100644 (file)
@@ -20,7 +20,7 @@ from sqlalchemy.orm.session import object_session, attribute_manager
 
 __all__ = ['relation', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer', 'extension', 
         'mapper', 'clear_mappers', 'compile_mappers', 'clear_mapper', 'class_mapper', 'object_mapper', 'MapperExtension', 'Query', 
-        'cascade_mappers', 'polymorphic_union', 'create_session', 'synonym', 'contains_eager', 'EXT_PASS', 'object_session'
+        'cascade_mappers', 'polymorphic_union', 'create_session', 'synonym', 'contains_alias', 'contains_eager', 'EXT_PASS', 'object_session'
         ]
 
 def relation(*args, **kwargs):
@@ -119,6 +119,34 @@ def noload(name):
     used with query.options()."""
     return strategies.EagerLazyOption(name, lazy=None)
 
+def contains_alias(alias):
+    """return a MapperOption that will indicate to the query that the main table
+    has been aliased.
+    
+    "alias" is the string name or Alias object representing the alias.
+    """
+    class AliasedRow(MapperExtension):
+        def __init__(self, alias):
+            self.alias = alias
+            if isinstance(self.alias, basestring):
+                self.selectable = None
+            else:
+                self.selectable = alias
+        def get_selectable(self, mapper):
+            if self.selectable is None:
+                self.selectable = mapper.mapped_table.alias(self.alias)
+            return self.selectable
+        def translate_row(self, mapper, context, row):
+            newrow = sautil.DictDecorator(row)
+            selectable = self.get_selectable(mapper)
+            for c in mapper.mapped_table.c:
+                c2 = selectable.corresponding_column(c, keys_ok=True, raiseerr=False)
+                if c2 and row.has_key(c2):
+                    newrow[c] = row[c2]
+            return newrow
+            
+    return ExtensionOption(AliasedRow(alias))
+    
 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.
index 4a46cc7d87946c4ab4dd71175fe92b0607058481..8c373e1b40562bf6c2734b1673bf2416744fa1a6 100644 (file)
@@ -1172,6 +1172,10 @@ class Mapper(object):
         either case, executes all the property loaders on the instance to also process extra
         information in the row."""
 
+        ret = context.extension.translate_row(self, context, row)
+        if ret is not EXT_PASS:
+            row = ret
+
         if not skip_polymorphic and self.polymorphic_on is not None:
             discriminator = row[self.polymorphic_on]
             if discriminator is not None:
@@ -1321,6 +1325,13 @@ class MapperExtension(object):
         the return value of this method is used as the result of query.select() if the
         value is anything other than EXT_PASS."""
         return EXT_PASS
+        
+    def translate_row(self, mapper, context, row):
+        """perform pre-processing on the given result row and return a new row instance.
+        
+        this is called as the very first step in the _instance() method."""
+        return EXT_PASS
+        
     def create_instance(self, mapper, selectcontext, row, class_):
         """receieve a row when a new object instance is about to be created from that row.  
         the method can choose to create the instance itself, or it can return 
@@ -1412,6 +1423,8 @@ class _ExtensionCarrier(MapperExtension):
         return self._do('select_by', *args, **kwargs)
     def select(self, *args, **kwargs):
         return self._do('select', *args, **kwargs)
+    def translate_row(self, *args, **kwargs):
+        return self._do('translate_row', *args, **kwargs)
     def create_instance(self, *args, **kwargs):
         return self._do('create_instance', *args, **kwargs)
     def append_result(self, *args, **kwargs):
index 77c90863a2ef82f35e6390b93b19fda802acafbb..9189f1f33bba3c369efdeb18a5bd33883dc4fbd2 100644 (file)
@@ -1052,6 +1052,19 @@ class EagerTest(MapperSuperTest):
             {'user_id' : 9, 'addresses' : (Address, [])}
             )
 
+    def testcustomfromalias(self):
+        mapper(User, users, properties={
+            'addresses':relation(Address, lazy=True)
+        })
+        mapper(Address, addresses)
+        query = users.select(users.c.user_id==7).union(users.select(users.c.user_id>7)).alias('ulist').outerjoin(addresses).select(use_labels=True)
+        q = create_session().query(User)
+        
+        def go():
+            l = q.options(contains_alias('ulist'), contains_eager('addresses')).instances(query.execute())
+            self.assert_result(l, User, *user_address_result)
+        self.assert_sql_count(testbase.db, go, 1)
+        
     def testcustomeagerquery(self):
         mapper(User, users, properties={
             # setting lazy=True - the contains_eager() option below