or Alias instance of an alias used in the query for the eagerly loaded child items.
easier to use than "decorator"
- 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
# 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
# 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())
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
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)
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)