diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2017-09-04 16:21:14 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2017-09-04 18:55:30 -0400 |
commit | 130f31ca79c7b40b2cb8aa1a4af7049408074d12 (patch) | |
tree | 503370647ea6d78bbc38e4e5e6c3343bdc710136 | |
parent | 65680b2343ef421a62582e23e2b35293732933ad (diff) | |
download | sqlalchemy-130f31ca79c7b40b2cb8aa1a4af7049408074d12.tar.gz |
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
-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 |