diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2007-09-29 20:17:40 +0000 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2007-09-29 20:17:40 +0000 |
| commit | 0dde728591cb083e351cf1ff1998aaf1883ab7a1 (patch) | |
| tree | 7bd294bc4f1060fcd7da8a1ecc1844f141b530f7 /lib | |
| parent | 4924007317f6d3cb9655b258c01ff888fb3f4a28 (diff) | |
| download | sqlalchemy-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.py | 20 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/strategies.py | 18 |
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) |
