diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2018-01-09 23:03:40 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2018-01-12 12:59:09 -0500 |
commit | a216625bd03313e85f8063c2c875730e15edc4a4 (patch) | |
tree | 96856673e2d31f177b65c52e9132c904638db43f /lib/sqlalchemy/orm/loading.py | |
parent | 3316890ffa9a51471ca7618e4a13e45494e03198 (diff) | |
download | sqlalchemy-a216625bd03313e85f8063c2c875730e15edc4a4.tar.gz |
Limit select in loading for correct types
Fixed bug in new "selectin" relationship loader where the loader could try
to load a non-existent relationship when loading a collection of
polymorphic objects, where only some of the mappers include that
relationship, typically when :meth:`.PropComparator.of_type` is being used.
This generalizes the mapper limiting that was present
in _load_subclass_via_in() to be part of the PostLoad object
itself, and is used by both polymorphic selectin loading and
relationship selectin loading.
Change-Id: I31416550e27bc8374b673860f57d9dcf96abe87d
Fixes: #4156
Diffstat (limited to 'lib/sqlalchemy/orm/loading.py')
-rw-r--r-- | lib/sqlalchemy/orm/loading.py | 24 |
1 files changed, 17 insertions, 7 deletions
diff --git a/lib/sqlalchemy/orm/loading.py b/lib/sqlalchemy/orm/loading.py index a23cafac2..8a20bf0dd 100644 --- a/lib/sqlalchemy/orm/loading.py +++ b/lib/sqlalchemy/orm/loading.py @@ -394,7 +394,8 @@ def _instance_processor( callable_ = _load_subclass_via_in(context, path, selectin_load_via) PostLoad.callable_for_path( - context, load_path, selectin_load_via, + context, load_path, selectin_load_via.mapper, + selectin_load_via, callable_, selectin_load_via) post_load = PostLoad.for_context(context, load_path, only_load_props) @@ -574,7 +575,6 @@ def _load_subclass_via_in(context, path, entity): primary_keys=[ state.key[1][0] if zero_idx else state.key[1] for state, load_attrs in states - if state.mapper.isa(mapper) ] ).all() @@ -738,16 +738,25 @@ class PostLoad(object): self.load_keys = None def add_state(self, state, overwrite): + # the states for a polymorphic load here are all shared + # within a single PostLoad object among multiple subtypes. + # Filtering of callables on a per-subclass basis needs to be done at + # the invocation level self.states[state] = overwrite def invoke(self, context, path): if not self.states: return path = path_registry.PathRegistry.coerce(path) - for key, loader, arg, kw in self.loaders.values(): + for token, limit_to_mapper, loader, arg, kw in self.loaders.values(): + states = [ + (state, overwrite) + for state, overwrite + in self.states.items() + if state.manager.mapper.isa(limit_to_mapper) + ] loader( - context, path, self.states.items(), - self.load_keys, *arg, **kw) + context, path, states, self.load_keys, *arg, **kw) self.states.clear() @classmethod @@ -764,12 +773,13 @@ class PostLoad(object): @classmethod def callable_for_path( - cls, context, path, attr_key, loader_callable, *arg, **kw): + cls, context, path, limit_to_mapper, token, + loader_callable, *arg, **kw): if path.path in context.post_load_paths: pl = context.post_load_paths[path.path] else: pl = context.post_load_paths[path.path] = PostLoad() - pl.loaders[attr_key] = (attr_key, loader_callable, arg, kw) + pl.loaders[token] = (token, limit_to_mapper, loader_callable, arg, kw) def load_scalar_attributes(mapper, state, attribute_names): |