summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2017-06-06 17:13:54 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2017-06-06 17:16:58 -0400
commit4e1f5377d1c5443016cc6c5ae86b8053ead41647 (patch)
tree423ed81611f61b9f2786a2d6f4a451aebe7bb003
parent6b68a70b5f903079f3c42a827daa3ea08a1a4b53 (diff)
downloadsqlalchemy-4e1f5377d1c5443016cc6c5ae86b8053ead41647.tar.gz
Warn when declared_attr.cascading detected on mapped class
A warning is emitted if the :attr:`.declared_attr.cascading` modifier is used with a declarative attribute that is itself declared on a class that is to be mapped, as opposed to a declarative mixin class or ``__abstract__`` class. The :attr:`.declared_attr.cascading` modifier currently only applies to mixin/abstract classes. Also add a test for @declared_attr.cascading when used on an attribute on __abstract__. Change-Id: Ib1b9dbe373e8be1cf24eadfed224a8988b3cd95d Fixes: #3847
-rw-r--r--doc/build/changelog/changelog_12.rst10
-rw-r--r--lib/sqlalchemy/ext/declarative/api.py6
-rw-r--r--lib/sqlalchemy/ext/declarative/base.py7
-rw-r--r--test/ext/declarative/test_basic.py17
-rw-r--r--test/ext/declarative/test_mixin.py24
5 files changed, 62 insertions, 2 deletions
diff --git a/doc/build/changelog/changelog_12.rst b/doc/build/changelog/changelog_12.rst
index 5ea6b513e..219742a31 100644
--- a/doc/build/changelog/changelog_12.rst
+++ b/doc/build/changelog/changelog_12.rst
@@ -27,6 +27,16 @@
:ref:`change_3853`
+ .. change:: 3847
+ :tags: bug, declarative
+ :tickets: 3847
+
+ A warning is emitted if the :attr:`.declared_attr.cascading` modifier
+ is used with a declarative attribute that is itself declared on
+ a class that is to be mapped, as opposed to a declarative mixin
+ class or ``__abstract__`` class. The :attr:`.declared_attr.cascading`
+ modifier currently only applies to mixin/abstract classes.
+
.. change:: 4003
:tags: feature, oracle
diff --git a/lib/sqlalchemy/ext/declarative/api.py b/lib/sqlalchemy/ext/declarative/api.py
index 7c503d471..ee13a90f3 100644
--- a/lib/sqlalchemy/ext/declarative/api.py
+++ b/lib/sqlalchemy/ext/declarative/api.py
@@ -199,6 +199,12 @@ class declared_attr(interfaces._MappedAttribute, property):
or MapperProperty-based declared attribute should be configured
distinctly per mapped subclass, within a mapped-inheritance scenario.
+ .. note::
+
+ The :attr:`.declared_attr.cascading` modifier **only** applies
+ to the use of :class:`.declared_attr` on declarative mixin classes
+ and ``__abstract__`` classes.
+
Below, both MyClass as well as MySubClass will have a distinct
``id`` Column object established::
diff --git a/lib/sqlalchemy/ext/declarative/base.py b/lib/sqlalchemy/ext/declarative/base.py
index d9433e692..3a2ac66ae 100644
--- a/lib/sqlalchemy/ext/declarative/base.py
+++ b/lib/sqlalchemy/ext/declarative/base.py
@@ -283,6 +283,13 @@ class _MapperConfig(object):
value = dict_[k]
if isinstance(value, declarative_props):
+ if isinstance(value, declared_attr) and value._cascading:
+ util.warn(
+ "Use of @declared_attr.cascading only applies to "
+ "Declarative 'mixin' and 'abstract' classes. "
+ "Currently, this flag is ignored on mapped class "
+ "%s" % self.cls)
+
value = getattr(cls, k)
elif isinstance(value, QueryableAttribute) and \
diff --git a/test/ext/declarative/test_basic.py b/test/ext/declarative/test_basic.py
index d91a88276..16fef5128 100644
--- a/test/ext/declarative/test_basic.py
+++ b/test/ext/declarative/test_basic.py
@@ -1,6 +1,6 @@
from sqlalchemy.testing import eq_, assert_raises, \
- assert_raises_message
+ assert_raises_message, expect_warnings
from sqlalchemy.ext import declarative as decl
from sqlalchemy import exc
import sqlalchemy as sa
@@ -1230,6 +1230,21 @@ class DeclarativeTest(DeclarativeTestBase):
assert 'somecol' in MyBase.__table__.c
assert 'somecol' not in MyClass.__table__.c
+ def test_decl_cascading_warns_non_mixin(self):
+ with expect_warnings(
+ "Use of @declared_attr.cascading only applies to "
+ "Declarative 'mixin' and 'abstract' classes. "
+ "Currently, this flag is ignored on mapped class "
+ "<class '.*.MyBase'>"
+ ):
+ class MyBase(Base):
+ __tablename__ = 'foo'
+ id = Column(Integer, primary_key=True)
+
+ @declared_attr.cascading
+ def somecol(cls):
+ return Column(Integer)
+
def test_column(self):
class User(Base, fixtures.ComparableEntity):
diff --git a/test/ext/declarative/test_mixin.py b/test/ext/declarative/test_mixin.py
index 3272e0753..be169bcda 100644
--- a/test/ext/declarative/test_mixin.py
+++ b/test/ext/declarative/test_mixin.py
@@ -1593,7 +1593,7 @@ class DeclaredAttrTest(DeclarativeTestBase, testing.AssertsCompiledSQL):
eq_(counter.mock_calls, [mock.call(A)])
- def test_property_cascade(self):
+ def test_property_cascade_mixin(self):
counter = mock.Mock()
class Mixin(object):
@@ -1613,6 +1613,28 @@ class DeclaredAttrTest(DeclarativeTestBase, testing.AssertsCompiledSQL):
eq_(counter.mock_calls, [mock.call(A), mock.call(B)])
+ def test_property_cascade_abstract(self):
+ counter = mock.Mock()
+
+ class Abs(Base):
+ __abstract__ = True
+
+ @declared_attr.cascading
+ def my_prop(cls):
+ counter(cls)
+ return column_property(cls.x + 2)
+
+ class A(Abs):
+ __tablename__ = 'a'
+
+ id = Column(Integer, primary_key=True)
+ x = Column(Integer)
+
+ class B(A):
+ pass
+
+ eq_(counter.mock_calls, [mock.call(A), mock.call(B)])
+
def test_col_prop_attrs_associated_w_class_for_mapper_args(self):
from sqlalchemy import Column
import collections