]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Raise informative error when non-object m2o comparison used
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 19 Apr 2020 20:52:54 +0000 (16:52 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 19 Apr 2020 20:55:03 +0000 (16:55 -0400)
An informative error message is raised when an ORM many-to-one comparison
is attempted against an object that is not an actual mapped instance.
Comparisons such as those to scalar subqueries aren't supported;
generalized comparison with subqueries is better achieved using
:meth:`~.RelationshipProperty.Comparator.has`.

Fixes: #5269
Change-Id: I2e23178eb59728c39241a46bfa7411239a87492e
(cherry picked from commit 430ce5eab26d46301ae741f9068f13ba09907d8e)

doc/build/changelog/unreleased_13/5269.rst [new file with mode: 0644]
lib/sqlalchemy/orm/relationships.py
test/orm/test_query.py

diff --git a/doc/build/changelog/unreleased_13/5269.rst b/doc/build/changelog/unreleased_13/5269.rst
new file mode 100644 (file)
index 0000000..90b50f5
--- /dev/null
@@ -0,0 +1,10 @@
+.. change::
+    :tags: bug, orm
+    :tickets: 5269
+
+    An informative error message is raised when an ORM many-to-one comparison
+    is attempted against an object that is not an actual mapped instance.
+    Comparisons such as those to scalar subqueries aren't supported;
+    generalized comparison with subqueries is better achieved using
+    :meth:`~.RelationshipProperty.Comparator.has`.
+
index 42a1b2826bfe12fed2b556c959d57a6943f1759b..daa85c5661787500835046df7d1bbc882f4577f3 100644 (file)
@@ -1610,8 +1610,19 @@ class RelationshipProperty(StrategizedProperty):
         alias_secondary=True,
     ):
         if state is not None:
-            state = attributes.instance_state(state)
+            try:
+                state = inspect(state)
+            except sa_exc.NoInspectionAvailable:
+                state = None
 
+            if state is None or not getattr(state, "is_instance", False):
+                raise sa_exc.ArgumentError(
+                    "Mapped instance expected for relationship "
+                    "comparison to object.   Classes, queries and other "
+                    "SQL elements are not accepted in this context; for "
+                    "comparison with a subquery, "
+                    "use %s.has(**criteria)." % self
+                )
         reverse_direction = not value_is_parent
 
         if state is None:
index 2a877015ee858cd9dc7bc62507b9346cf7408d1b..77a820953ceea6f4a57107c42d5bade9f0e4801b 100644 (file)
@@ -2947,7 +2947,44 @@ class FilterTest(QueryTest, AssertsCompiledSQL):
             [Order(id=3)],
         )
 
-    def test_comparison(self):
+    @testing.combinations(
+        lambda sess, User, Address: (
+            sess.query(Address).filter(
+                Address.user == sess.query(User).as_scalar()
+            )
+        ),
+        lambda sess, User, Address: (
+            sess.query(Address).filter_by(user=sess.query(User).as_scalar())
+        ),
+        lambda sess, User, Address: (
+            sess.query(Address).filter(Address.user == sess.query(User))
+        ),
+        lambda sess, User, Address: (
+            sess.query(Address).filter(
+                Address.user == sess.query(User).subquery()
+            )
+        ),
+        lambda sess, User, Address: (
+            sess.query(Address).filter_by(user="foo")
+        ),
+    )
+    def test_object_comparison_needs_object(self, fn):
+        User, Address = (
+            self.classes.User,
+            self.classes.Address,
+        )
+
+        sess = create_session()
+        assert_raises_message(
+            sa.exc.ArgumentError,
+            "Mapped instance expected for relationship comparison to object.",
+            fn,
+            sess,
+            User,
+            Address,
+        ),
+
+    def test_object_comparison(self):
         """test scalar comparison to an object instance"""
 
         Item, Order, Dingaling, User, Address = (