summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/sqlalchemy/ext/declarative/base.py2
-rw-r--r--test/ext/declarative/test_mixin.py53
2 files changed, 55 insertions, 0 deletions
diff --git a/lib/sqlalchemy/ext/declarative/base.py b/lib/sqlalchemy/ext/declarative/base.py
index 6735abf4c..d19257366 100644
--- a/lib/sqlalchemy/ext/declarative/base.py
+++ b/lib/sqlalchemy/ext/declarative/base.py
@@ -202,6 +202,7 @@ class _MapperConfig(object):
if not oldclassprop and obj._cascading:
dict_[name] = column_copies[obj] = \
ret = obj.__get__(obj, cls)
+ setattr(cls, name, ret)
else:
if oldclassprop:
util.warn_deprecated(
@@ -439,6 +440,7 @@ class _MapperConfig(object):
def _prepare_mapper_arguments(self):
properties = self.properties
+
if self.mapper_args_fn:
mapper_args = self.mapper_args_fn()
else:
diff --git a/test/ext/declarative/test_mixin.py b/test/ext/declarative/test_mixin.py
index db86927a1..6dabfcd22 100644
--- a/test/ext/declarative/test_mixin.py
+++ b/test/ext/declarative/test_mixin.py
@@ -1432,6 +1432,59 @@ class DeclaredAttrTest(DeclarativeTestBase, testing.AssertsCompiledSQL):
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
+
+ asserted = collections.defaultdict(set)
+
+ class Mixin(object):
+ @declared_attr.cascading
+ def my_attr(cls):
+ if decl.has_inherited_table(cls):
+ id = Column(ForeignKey('a.my_attr'), primary_key=True)
+ asserted['b'].add(id)
+ else:
+ id = Column(Integer, primary_key=True)
+ asserted['a'].add(id)
+ return id
+
+ class A(Base, Mixin):
+ __tablename__ = 'a'
+
+ @declared_attr
+ def __mapper_args__(cls):
+ asserted['a'].add(cls.my_attr)
+ return {}
+
+ # here:
+ # 1. A is mapped. so A.my_attr is now the InstrumentedAttribute.
+ # 2. B wants to call my_attr also. Due to .cascading, it has been
+ # invoked specific to B, and is present in the dict_ that will
+ # be used when we map the class. But except for the
+ # special setattr() we do in _scan_attributes() in this case, would
+ # otherwise not been set on the class as anything from this call;
+ # the usual mechanics of calling it from the descriptor also do not
+ # work because A is fully mapped and because A set it up, is currently
+ # that non-expected InstrumentedAttribute and replaces the
+ # descriptor from being invoked.
+
+ class B(A):
+ __tablename__ = 'b'
+
+ @declared_attr
+ def __mapper_args__(cls):
+ asserted['b'].add(cls.my_attr)
+ return {}
+
+ eq_(
+ asserted,
+ {
+ 'a': set([A.my_attr.property.columns[0]]),
+ 'b': set([B.my_attr.property.columns[0]])
+ }
+ )
+
def test_column_pre_map(self):
counter = mock.Mock()