summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2007-09-29 20:17:40 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2007-09-29 20:17:40 +0000
commit0dde728591cb083e351cf1ff1998aaf1883ab7a1 (patch)
tree7bd294bc4f1060fcd7da8a1ecc1844f141b530f7 /lib
parent4924007317f6d3cb9655b258c01ff888fb3f4a28 (diff)
downloadsqlalchemy-0dde728591cb083e351cf1ff1998aaf1883ab7a1.tar.gz
- fixed three- and multi-level select and deferred inheritance
loading (i.e. abc inheritance with no select_table), [ticket:795]
Diffstat (limited to 'lib')
-rw-r--r--lib/sqlalchemy/orm/mapper.py20
-rw-r--r--lib/sqlalchemy/orm/strategies.py18
2 files changed, 28 insertions, 10 deletions
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index 9764a0ae6..b2bffd6ea 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -1442,10 +1442,7 @@ class Mapper(object):
return instance
- def _deferred_inheritance_condition(self, needs_tables):
- cond = self.inherit_condition
-
- param_names = []
+ def _deferred_inheritance_condition(self, base_mapper, needs_tables):
def visit_binary(binary):
leftcol = binary.left
rightcol = binary.right
@@ -1457,8 +1454,17 @@ class Mapper(object):
elif rightcol not in needs_tables:
binary.right = sql.bindparam(rightcol.name, None, type_=binary.right.type, unique=True)
param_names.append(rightcol)
- cond = mapperutil.BinaryVisitor(visit_binary).traverse(cond, clone=True)
- return cond, param_names
+
+ allconds = []
+ param_names = []
+
+ visitor = mapperutil.BinaryVisitor(visit_binary)
+ for mapper in self.iterate_to_root():
+ if mapper is base_mapper:
+ break
+ allconds.append(visitor.traverse(mapper.inherit_condition, clone=True))
+
+ return sql.and_(*allconds), param_names
def translate_row(self, tomapper, row):
"""Translate the column keys of a row into a new or proxied
@@ -1532,7 +1538,7 @@ class Mapper(object):
if hosted_mapper is None or len(needs_tables)==0 or hosted_mapper.polymorphic_fetch == 'deferred':
return
- cond, param_names = self._deferred_inheritance_condition(needs_tables)
+ cond, param_names = self._deferred_inheritance_condition(hosted_mapper, needs_tables)
statement = sql.select(needs_tables, cond, use_labels=True)
def post_execute(instance, **flags):
self.__log_debug("Post query loading instance " + mapperutil.instance_str(instance))
diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py
index b93993af8..09b51c203 100644
--- a/lib/sqlalchemy/orm/strategies.py
+++ b/lib/sqlalchemy/orm/strategies.py
@@ -89,7 +89,7 @@ class ColumnLoader(LoaderStrategy):
# 'deferred' polymorphic row fetcher, put a callable on the property.
def new_execute(instance, row, isnew, **flags):
if isnew:
- sessionlib.attribute_manager.init_instance_attribute(instance, self.key, callable_=self._get_deferred_inheritance_loader(instance, mapper, needs_tables))
+ sessionlib.attribute_manager.init_instance_attribute(instance, self.key, callable_=self._get_deferred_inheritance_loader(instance, mapper, hosted_mapper, needs_tables))
if self._should_log_debug:
self.logger.debug("Returning deferred column fetcher for %s %s" % (mapper, self.key))
return (new_execute, None, None)
@@ -99,18 +99,30 @@ class ColumnLoader(LoaderStrategy):
self.logger.debug("Returning no column fetcher for %s %s" % (mapper, self.key))
return (None, None, None)
- def _get_deferred_inheritance_loader(self, instance, mapper, needs_tables):
+ def _get_deferred_inheritance_loader(self, instance, mapper, hosted_mapper, needs_tables):
+ # create a deferred column loader which will query the remaining not-yet-loaded tables in an inheritance load.
+ # the mapper for the object creates the WHERE criterion using the mapper who originally
+ # "hosted" the query and the list of tables which are unloaded between the "hosted" mapper
+ # and this mapper. (i.e. A->B->C, the query used mapper A. therefore will need B's and C's tables
+ # in the query).
def create_statement():
- cond, param_names = mapper._deferred_inheritance_condition(needs_tables)
+ # TODO: the SELECT statement here should be cached in the selectcontext. we are somewhat duplicating
+ # efforts from mapper._get_poly_select_loader as well and should look
+ # for ways to simplify.
+ cond, param_names = mapper._deferred_inheritance_condition(hosted_mapper, needs_tables)
statement = sql.select(needs_tables, cond, use_labels=True)
params = {}
for c in param_names:
params[c.name] = mapper.get_attr_by_column(instance, c)
return (statement, params)
+ # install the create_statement() callable using the deferred loading strategy
strategy = self.parent_property._get_strategy(DeferredColumnLoader)
+ # assemble list of all ColumnProperties which will need to be loaded
props = [p for p in mapper.iterate_properties if isinstance(p.strategy, ColumnLoader) and p.columns[0].table in needs_tables]
+
+ # set the deferred loader on the instance attribute
return strategy.setup_loader(instance, props=props, create_statement=create_statement)