- 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
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).
__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):
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.
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:
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
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):
{'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