]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Check for non-mapped property in synonym
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 4 Sep 2017 20:21:14 +0000 (16:21 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 4 Sep 2017 22:55:30 +0000 (18:55 -0400)
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
doc/build/changelog/unreleased_11/4067.rst [new file with mode: 0644]
lib/sqlalchemy/orm/descriptor_props.py
test/orm/test_mapper.py

diff --git a/doc/build/changelog/unreleased_11/4067.rst b/doc/build/changelog/unreleased_11/4067.rst
new file mode 100644 (file)
index 0000000..8fa8b92
--- /dev/null
@@ -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.
index 9afdbf693c6f03caf0a80c5f9a5725a99c8b7444..b9f016b42021732dad1fa8bcabec1fb3c38d13ff 100644 (file)
@@ -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
index 2392aa002643595722204d212f9f4af4e9097d7a..f39e174b065dc064d9fcf7bf8c3c5f9dc14e0a05 100644 (file)
@@ -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