]> 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:52:54 +0000 (16:52 -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

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 7d33c4649810153cf83ba7d22132916115a46533..6ac56a324a81542a2b7f65983ae3d542d393f9c4 100644 (file)
@@ -1621,8 +1621,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 a5814810859c533b6bf83017423ea0676db4b984..ce9a6e3c5506e425c094d41e2f836212ca93caf6 100644 (file)
@@ -3093,7 +3093,46 @@ 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).scalar_subquery()
+            )
+        ),
+        lambda sess, User, Address: (
+            sess.query(Address).filter_by(
+                user=sess.query(User).scalar_subquery()
+            )
+        ),
+        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 = (