]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Warn when declared_attr.cascading detected on mapped class
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 6 Jun 2017 21:13:54 +0000 (17:13 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 6 Jun 2017 21:16:58 +0000 (17:16 -0400)
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
doc/build/changelog/changelog_12.rst
lib/sqlalchemy/ext/declarative/api.py
lib/sqlalchemy/ext/declarative/base.py
test/ext/declarative/test_basic.py
test/ext/declarative/test_mixin.py

index 5ea6b513ed0c31cabc5974ffd51869c71f922aca..219742a3161b4e148aea32f9e4b37e985dbfd30c 100644 (file)
 
             :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
 
index 7c503d471b0acb1b50824738871dd23a1e2ee52b..ee13a90f3c2a4b3a0df772f9eee8815eb052a17b 100644 (file)
@@ -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::
 
index d9433e6928c40fe922b7deca69ceb9677a3f7aad..3a2ac66aee4b1c6d923b9e9796d1569a8f6a1ba1 100644 (file)
@@ -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 \
index d91a88276aee32bce27f432104c5354de372e857..16fef5128d8ebb96fe49bba4c9a98adde5063f16 100644 (file)
@@ -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):
index 3272e0753f817dbbd137f4bc624f717d16ddc476..be169bcda19aeaeb0b3eb0fc3367d16246ef8032 100644 (file)
@@ -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