- 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"
This commit is contained in:
Mike Bayer
2007-02-20 01:04:07 +00:00
parent eb311db5ec
commit ae000fb550
5 changed files with 59 additions and 18 deletions
+3
View File
@@ -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
+2 -11
View 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())
+13 -6
View 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
+11 -1
View 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)
+30
View 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)