summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/build/changelog/unreleased_11/4067.rst9
-rw-r--r--lib/sqlalchemy/orm/descriptor_props.py13
-rw-r--r--test/orm/test_mapper.py25
3 files changed, 46 insertions, 1 deletions
diff --git a/doc/build/changelog/unreleased_11/4067.rst b/doc/build/changelog/unreleased_11/4067.rst
new file mode 100644
index 000000000..8fa8b9229
--- /dev/null
+++ b/doc/build/changelog/unreleased_11/4067.rst
@@ -0,0 +1,9 @@
+.. change::
+ :tags: bug, orm
+ :tickets: 4067
+ :versions: 1.2.0b3
+
+ An :class:`.InvalidRequestError` is raised when a :func:`.synonym`
+ is used against an attribute that is not against a :class:`.MapperProperty`,
+ such as an association proxy. Previously, a recursion overflow would
+ occur trying to locate non-existent attributes.
diff --git a/lib/sqlalchemy/orm/descriptor_props.py b/lib/sqlalchemy/orm/descriptor_props.py
index 9afdbf693..b9f016b42 100644
--- a/lib/sqlalchemy/orm/descriptor_props.py
+++ b/lib/sqlalchemy/orm/descriptor_props.py
@@ -594,7 +594,18 @@ class SynonymProperty(DescriptorProperty):
@util.memoized_property
def _proxied_property(self):
- return getattr(self.parent.class_, self.name).property
+ attr = getattr(self.parent.class_, self.name)
+ if not hasattr(attr, 'property') or not \
+ isinstance(attr.property, MapperProperty):
+ raise sa_exc.InvalidRequestError(
+ """synonym() attribute "%s.%s" only supports """
+ """ORM mapped attributes, got %r""" % (
+ self.parent.class_.__name__,
+ self.name,
+ attr
+ )
+ )
+ return attr.property
def _comparator_factory(self, mapper):
prop = self._proxied_property
diff --git a/test/orm/test_mapper.py b/test/orm/test_mapper.py
index 2392aa002..f39e174b0 100644
--- a/test/orm/test_mapper.py
+++ b/test/orm/test_mapper.py
@@ -1406,6 +1406,31 @@ class MapperTest(_fixtures.FixtureTest, AssertsCompiledSQL):
u = s.query(User).filter(User.y == 8).one()
eq_(u.y, 8)
+ def test_synonym_of_non_property_raises(self):
+ from sqlalchemy.ext.associationproxy import association_proxy
+
+ class User(object):
+ pass
+
+ users, Address, addresses = (
+ self.tables.users,
+ self.classes.Address,
+ self.tables.addresses)
+
+ mapper(User, users, properties={
+ 'y': synonym('x'),
+ 'addresses': relationship(Address)
+ })
+ mapper(Address, addresses)
+ User.x = association_proxy("addresses", "email_address")
+
+ assert_raises_message(
+ sa.exc.InvalidRequestError,
+ r'synonym\(\) attribute "User.x" only supports ORM mapped '
+ 'attributes, got .*AssociationProxy',
+ getattr, User.y, "property"
+ )
+
def test_synonym_column_location(self):
users, User = self.tables.users, self.classes.User