From: Mike Bayer Date: Mon, 4 Sep 2017 20:21:14 +0000 (-0400) Subject: Check for non-mapped property in synonym X-Git-Tag: rel_1_1_14~3^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8ec1369eb3dca6cff1d329a11066559e8aba38c5;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Check for non-mapped property in synonym 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. Change-Id: If2ce38c429a69951df4c94b71b74edbd59d775e3 Fixes: #4067 (cherry picked from commit 130f31ca79c7b40b2cb8aa1a4af7049408074d12) --- diff --git a/doc/build/changelog/unreleased_11/4067.rst b/doc/build/changelog/unreleased_11/4067.rst new file mode 100644 index 0000000000..8fa8b92290 --- /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 0792ff2e2c..863e7757e2 100644 --- a/lib/sqlalchemy/orm/descriptor_props.py +++ b/lib/sqlalchemy/orm/descriptor_props.py @@ -574,7 +574,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 65f3ffd2ce..42d114f6fa 100644 --- a/test/orm/test_mapper.py +++ b/test/orm/test_mapper.py @@ -1381,6 +1381,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