summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2019-01-14 18:53:58 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2019-01-14 21:26:35 -0500
commit836178d42620869c3cab4b7f41d24560f0098c87 (patch)
tree362fe080e1a41295f5f12561e77e46b009e77de3 /lib/sqlalchemy
parent5f697cb6729761cc0422597339f7b5e41382b803 (diff)
downloadsqlalchemy-836178d42620869c3cab4b7f41d24560f0098c87.tar.gz
Relax "ambiguous" association proxy restrictions, support Proxy
Fixed issue in association proxy due to :ticket:`3423` which caused the use of custom :class:`.PropComparator` objects with hybrid attribites, such as the one demonstrated in the ``dictlike-polymorphic`` example to not function within an association proxy. The strictness that was added in :ticket:`3423` has been relaxed, and additional logic to accomodate for an association proxy that links to a custom hybrid have been added. Fixes: #4446 Change-Id: I8addc80f51094769915ac2dce1a301bd72ee7433
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/ext/associationproxy.py42
1 files changed, 25 insertions, 17 deletions
diff --git a/lib/sqlalchemy/ext/associationproxy.py b/lib/sqlalchemy/ext/associationproxy.py
index 59ed1aa99..5b05d2b3d 100644
--- a/lib/sqlalchemy/ext/associationproxy.py
+++ b/lib/sqlalchemy/ext/associationproxy.py
@@ -380,7 +380,12 @@ class AssociationProxyInstance(object):
parent, owning_class, target_class, value_attr
)
- is_object = getattr(target_class, value_attr).impl.uses_objects
+ attr = getattr(target_class, value_attr)
+ if attr._is_internal_proxy and not hasattr(attr, "impl"):
+ return AmbiguousAssociationProxyInstance(
+ parent, owning_class, target_class, value_attr
+ )
+ is_object = attr.impl.uses_objects
if is_object:
return ObjectAssociationProxyInstance(
parent, owning_class, target_class, value_attr
@@ -739,13 +744,10 @@ class AmbiguousAssociationProxyInstance(AssociationProxyInstance):
)
def get(self, obj):
- self._ambiguous()
-
- def set(self, obj, values):
- self._ambiguous()
-
- def delete(self, obj):
- self._ambiguous()
+ if obj is None:
+ self._ambiguous()
+ else:
+ return super(AmbiguousAssociationProxyInstance, self).get(obj)
def any(self, criterion=None, **kwargs):
self._ambiguous()
@@ -764,25 +766,31 @@ class AmbiguousAssociationProxyInstance(AssociationProxyInstance):
if parent_instance is not None:
actual_obj = getattr(parent_instance, self.target_collection)
if actual_obj is not None:
- instance_class = type(actual_obj)
- if instance_class not in self._lookup_cache:
- self._populate_cache(instance_class)
-
try:
- return self._lookup_cache[instance_class]
- except KeyError:
+ insp = inspect(actual_obj)
+ except exc.NoInspectionAvailable:
pass
+ else:
+ mapper = insp.mapper
+ instance_class = mapper.class_
+ if instance_class not in self._lookup_cache:
+ self._populate_cache(instance_class, mapper)
+
+ try:
+ return self._lookup_cache[instance_class]
+ except KeyError:
+ pass
# no object or ambiguous object given, so return "self", which
- # is a no-op proxy.
+ # is a proxy with generally only instance-level functionality
return self
- def _populate_cache(self, instance_class):
+ def _populate_cache(self, instance_class, mapper):
prop = orm.class_mapper(self.owning_class).get_property(
self.target_collection
)
- if inspect(instance_class).mapper.isa(prop.mapper):
+ if mapper.isa(prop.mapper):
target_class = instance_class
try:
target_assoc = self._cls_unwrap_target_assoc_proxy(