summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2018-02-15 17:42:48 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2018-02-15 17:59:15 -0500
commit283a969d68688018abee04fde479a6fd8495b6f6 (patch)
tree96faee92575ba64df3c2d641f4c77bae6c8a231d /lib
parent6c6a7720f221d255a9c8abaec24fc5272403019b (diff)
downloadsqlalchemy-283a969d68688018abee04fde479a6fd8495b6f6.tar.gz
Test attributes for being non-mapped column properties more closely
Fixed bug in concrete inheritance mapping where user-defined attributes such as hybrid properties that mirror the names of mapped attributes from sibling classes would be overwritten by the mapper as non-accessible at the instance level. Also ensured that user-bound descriptors are not implicitly invoked at the class level during the mapper configuration stage. Change-Id: I52b84a15c296b14efeaffb72941fc941d1d52c0d Fixes: #4188
Diffstat (limited to 'lib')
-rw-r--r--lib/sqlalchemy/orm/instrumentation.py9
-rw-r--r--lib/sqlalchemy/orm/mapper.py26
2 files changed, 28 insertions, 7 deletions
diff --git a/lib/sqlalchemy/orm/instrumentation.py b/lib/sqlalchemy/orm/instrumentation.py
index d5422b0d0..1b839cf5c 100644
--- a/lib/sqlalchemy/orm/instrumentation.py
+++ b/lib/sqlalchemy/orm/instrumentation.py
@@ -168,6 +168,15 @@ class ClassManager(dict):
if isinstance(val, interfaces.InspectionAttr):
yield key, val
+ def _get_class_attr_mro(self, key, default=None):
+ """return an attribute on the class without tripping it."""
+
+ for supercls in self.class_.__mro__:
+ if key in supercls.__dict__:
+ return supercls.__dict__[key]
+ else:
+ return default
+
def _attr_has_impl(self, key):
"""Return True if the given attribute is fully initialized.
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index cd9e00b8b..a30a8c243 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -1613,10 +1613,23 @@ class Mapper(InspectionAttr):
if not self.concrete:
self._configure_property(key, prop, init=False, setparent=False)
elif key not in self._props:
- self._configure_property(
- key,
- properties.ConcreteInheritedProperty(),
- init=init, setparent=True)
+ # determine if the class implements this attribute; if not,
+ # or if it is implemented by the attribute that is handling the
+ # given superclass-mapped property, then we need to report that we
+ # can't use this at the instance level since we are a concrete
+ # mapper and we don't map this. don't trip user-defined
+ # descriptors that might have side effects when invoked.
+ implementing_attribute = self.class_manager._get_class_attr_mro(
+ key, prop)
+ if implementing_attribute is prop or (isinstance(
+ implementing_attribute,
+ attributes.InstrumentedAttribute) and
+ implementing_attribute._parententity is prop.parent
+ ):
+ self._configure_property(
+ key,
+ properties.ConcreteInheritedProperty(),
+ init=init, setparent=True)
def _configure_property(self, key, prop, init=True, setparent=True):
self._log("_configure_property(%s, %s)", key, prop.__class__.__name__)
@@ -2413,9 +2426,8 @@ class Mapper(InspectionAttr):
self.class_.__dict__[assigned_name]):
return True
else:
- if getattr(self.class_, assigned_name, None) is not None \
- and self._is_userland_descriptor(
- getattr(self.class_, assigned_name)):
+ attr = self.class_manager._get_class_attr_mro(assigned_name, None)
+ if attr is not None and self._is_userland_descriptor(attr):
return True
if self.include_properties is not None and \