From: Mike Bayer Date: Tue, 20 Feb 2007 02:12:46 +0000 (+0000) Subject: - added "contains_alias()" option for result set mapping to an alias of the mapped... X-Git-Tag: rel_0_3_5~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fa46e0ec667bcaeb95f54280efb61be799977e54;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - added "contains_alias()" option for result set mapping to an alias of the mapped table --- diff --git a/CHANGES b/CHANGES index 494cc8d3e7..f9db501f45 100644 --- 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 diff --git a/doc/build/content/adv_datamapping.txt b/doc/build/content/adv_datamapping.txt index cf49dc3410..d6102b424a 100644 --- a/doc/build/content/adv_datamapping.txt +++ b/doc/build/content/adv_datamapping.txt @@ -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). diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py index ac9272a173..30070e8d72 100644 --- a/lib/sqlalchemy/orm/__init__.py +++ b/lib/sqlalchemy/orm/__init__.py @@ -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. diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 4a46cc7d87..8c373e1b40 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -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): diff --git a/test/orm/mapper.py b/test/orm/mapper.py index 77c90863a2..9189f1f33b 100644 --- a/test/orm/mapper.py +++ b/test/orm/mapper.py @@ -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