diff options
-rw-r--r-- | doc/build/changelog/unreleased_11/4067.rst | 9 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/descriptor_props.py | 13 | ||||
-rw-r--r-- | test/orm/test_mapper.py | 25 |
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 |