summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/ext/declarative/base.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2017-09-26 11:03:07 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2017-09-26 11:36:08 -0400
commitec1700ba29f7f15859ee6576855a4d6675265640 (patch)
tree2b2f19bbcc5906139a1b88c527d46e64eceaf4bd /lib/sqlalchemy/ext/declarative/base.py
parent1b0b35f254f545dbeb3ad6e2215ba24ae1c02894 (diff)
downloadsqlalchemy-ticket_4091.tar.gz
Warnings for @declared_attr.cascadingticket_4091
A warning is emitted if a subclass attempts to override an attribute that was declared on a superclass using ``@declared_attr.cascading`` that the overridden attribute will be ignored. This use case cannot be fully supported down to further subclasses without more complex development efforts, so for consistency the "cascading" is honored all the way down regardless of overriding attributes. A warning is emitted if the ``@declared_attr.cascading`` attribute is used with a special declarative name such as ``__tablename__``, as this has no effect. Ensure that documenation refers to the current inconsistency that __tablename__ can be overridden by subclasses however @declared_attr.cascading cannot. Fixes: #4091 Fixes: #4092 Change-Id: I3aecdb2f99d408e404a1223f5ad86ae3c7fdf036
Diffstat (limited to 'lib/sqlalchemy/ext/declarative/base.py')
-rw-r--r--lib/sqlalchemy/ext/declarative/base.py39
1 files changed, 35 insertions, 4 deletions
diff --git a/lib/sqlalchemy/ext/declarative/base.py b/lib/sqlalchemy/ext/declarative/base.py
index 3a2ac66ae..255373597 100644
--- a/lib/sqlalchemy/ext/declarative/base.py
+++ b/lib/sqlalchemy/ext/declarative/base.py
@@ -88,6 +88,19 @@ def _as_declarative(cls, classname, dict_):
_MapperConfig.setup_mapping(cls, classname, dict_)
+def _check_declared_props_nocascade(obj, name, cls):
+
+ if isinstance(obj, declarative_props):
+ if getattr(obj, '_cascading', False):
+ util.warn(
+ "@declared_attr.cascading is not supported on the %s "
+ "attribute on class %s. This attribute invokes for "
+ "subclasses in any case." % (name, cls))
+ return True
+ else:
+ return False
+
+
class _MapperConfig(object):
@classmethod
@@ -167,9 +180,11 @@ class _MapperConfig(object):
for name, obj in vars(base).items():
if name == '__mapper_args__':
+ check_decl = \
+ _check_declared_props_nocascade(obj, name, cls)
if not mapper_args_fn and (
not class_mapped or
- isinstance(obj, declarative_props)
+ check_decl
):
# don't even invoke __mapper_args__ until
# after we've determined everything about the
@@ -177,17 +192,21 @@ class _MapperConfig(object):
# make a copy of it so a class-level dictionary
# is not overwritten when we update column-based
# arguments.
- mapper_args_fn = lambda: dict(cls.__mapper_args__)
+ mapper_args_fn = lambda: dict(cls.__mapper_args__) # noqa
elif name == '__tablename__':
+ check_decl = \
+ _check_declared_props_nocascade(obj, name, cls)
if not tablename and (
not class_mapped or
- isinstance(obj, declarative_props)
+ check_decl
):
tablename = cls.__tablename__
elif name == '__table_args__':
+ check_decl = \
+ _check_declared_props_nocascade(obj, name, cls)
if not table_args and (
not class_mapped or
- isinstance(obj, declarative_props)
+ check_decl
):
table_args = cls.__table_args__
if not isinstance(
@@ -220,6 +239,18 @@ class _MapperConfig(object):
elif isinstance(obj, declarative_props):
oldclassprop = isinstance(obj, util.classproperty)
if not oldclassprop and obj._cascading:
+ if name in dict_:
+ # unfortunately, while we can use the user-
+ # defined attribute here to allow a clean
+ # override, if there's another
+ # subclass below then it still tries to use
+ # this. not sure if there is enough information
+ # here to add this as a feature later on.
+ util.warn(
+ "Attribute '%s' on class %s cannot be "
+ "processed due to "
+ "@declared_attr.cascading; "
+ "skipping" % (name, cls))
dict_[name] = column_copies[obj] = \
ret = obj.__get__(obj, cls)
setattr(cls, name, ret)