]> 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:56:38 +0000 (18:56 -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
(cherry picked from commit 130f31ca79c7b40b2cb8aa1a4af7049408074d12)

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 0792ff2e2c0e62ae33ee8b416cae76494374ca68..863e7757e2bc417253f6b4f5c1f97c89059580ee 100644 (file)
@@ -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
index 65f3ffd2ce42767287c4d24cca4680c8961ddfe3..42d114f6faf2b8b58bc111d8b377d0abbb754b9e 100644 (file)
@@ -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