diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-01-26 19:49:44 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-02-04 10:37:45 -0500 |
commit | 95c371f1d3007071a32fad1d67329e6f9d56931b (patch) | |
tree | 8f8d1ef868dbf1b623bbd1d4ad9f25db5d65a8c4 /lib/sqlalchemy/orm/util.py | |
parent | 7e48d8b2a70b399ffa6ec874f824d6cee82980a0 (diff) | |
download | sqlalchemy-95c371f1d3007071a32fad1d67329e6f9d56931b.tar.gz |
Improve support for with_polymorphic in mapper options
Improved the behavior of :func:`.orm.with_polymorphic` in conjunction with
loader options, in particular wildcard operations as well as
:func:`.orm.load_only`. The polymorphic object will be more accurately
targeted so that column-level options on the entity will correctly take
effect.The issue is a continuation of the same kinds of things fixed in
:ticket:`4468`.
The path logic when using chained mapper options is improved
to be more accurate in terms of the entities being linked
in the path; when using :func:`.with_polymorphic`, mapper
options against this entity need to specify attributes
in terms of the with_polymorphic() object and not against the
base mappings. New error conditions are raised which were previously
more than likely silenty failures.
Fixes: #4469
Change-Id: Ie8d802879663b4ff6f6ac1438c885c06d78ae2a0
Diffstat (limited to 'lib/sqlalchemy/orm/util.py')
-rw-r--r-- | lib/sqlalchemy/orm/util.py | 84 |
1 files changed, 68 insertions, 16 deletions
diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py index 92dd4c4ec..f9258895d 100644 --- a/lib/sqlalchemy/orm/util.py +++ b/lib/sqlalchemy/orm/util.py @@ -589,12 +589,32 @@ class AliasedInsp(InspectionAttr): self.persist_selectable ) = self.local_table = selectable self.name = name - self.with_polymorphic_mappers = with_polymorphic_mappers self.polymorphic_on = polymorphic_on self._base_alias = _base_alias or self self._use_mapper_path = _use_mapper_path self.represents_outer_join = represents_outer_join + if with_polymorphic_mappers: + self._is_with_polymorphic = True + self.with_polymorphic_mappers = with_polymorphic_mappers + self._with_polymorphic_entities = [] + for poly in self.with_polymorphic_mappers: + if poly is not mapper: + ent = AliasedClass( + poly.class_, + selectable, + base_alias=self, + adapt_on_names=adapt_on_names, + use_mapper_path=_use_mapper_path, + ) + + setattr(self.entity, poly.class_.__name__, ent) + self._with_polymorphic_entities.append(ent._aliased_insp) + + else: + self._is_with_polymorphic = False + self.with_polymorphic_mappers = [mapper] + self._adapter = sql_util.ColumnAdapter( selectable, equivalents=mapper._equivalent_columns, @@ -605,20 +625,6 @@ class AliasedInsp(InspectionAttr): self._adapt_on_names = adapt_on_names self._target = mapper.class_ - for poly in self.with_polymorphic_mappers: - if poly is not mapper: - setattr( - self.entity, - poly.class_.__name__, - AliasedClass( - poly.class_, - selectable, - base_alias=self, - adapt_on_names=adapt_on_names, - use_mapper_path=_use_mapper_path, - ), - ) - is_aliased_class = True "always returns True" @@ -718,7 +724,17 @@ class AliasedInsp(InspectionAttr): ) def __str__(self): - return "aliased(%s)" % (self._target.__name__,) + if self._is_with_polymorphic: + return "with_polymorphic(%s, [%s])" % ( + self._target.__name__, + ", ".join( + mp.class_.__name__ + for mp in self.with_polymorphic_mappers + if mp is not self.mapper + ), + ) + else: + return "aliased(%s)" % (self._target.__name__,) inspection._inspects(AliasedClass)(lambda target: target._aliased_insp) @@ -1225,6 +1241,42 @@ def _entity_corresponds_to(given, entity): return entity.common_parent(given) +def _entity_corresponds_to_use_path_impl(given, entity): + """determine if 'given' corresponds to 'entity', in terms + of a path of loader options where a mapped attribute is taken to + be a member of a parent entity. + + e.g.:: + + someoption(A).someoption(A.b) # -> fn(A, A) -> True + someoption(A).someoption(C.d) # -> fn(A, C) -> False + + a1 = aliased(A) + someoption(a1).someoption(A.b) # -> fn(a1, A) -> False + someoption(a1).someoption(a1.b) # -> fn(a1, a1) -> True + + wp = with_polymorphic(A, [A1, A2]) + someoption(wp).someoption(A1.foo) # -> fn(wp, A1) -> False + someoption(wp).someoption(wp.A1.foo) # -> fn(wp, wp.A1) -> True + + + """ + if given.is_aliased_class: + return ( + entity.is_aliased_class + and not entity._use_mapper_path + and given is entity + or given in entity._with_polymorphic_entities + ) + elif not entity.is_aliased_class: + return given.common_parent(entity.mapper) + else: + return ( + entity._use_mapper_path + and given in entity.with_polymorphic_mappers + ) + + def _entity_isa(given, mapper): """determine if 'given' "is a" mapper, in terms of the given would load rows of type 'mapper'. |