summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/build/changelog/unreleased_13/5269.rst10
-rw-r--r--lib/sqlalchemy/orm/relationships.py13
-rw-r--r--test/orm/test_query.py41
3 files changed, 62 insertions, 2 deletions
diff --git a/doc/build/changelog/unreleased_13/5269.rst b/doc/build/changelog/unreleased_13/5269.rst
new file mode 100644
index 000000000..90b50f5dc
--- /dev/null
+++ b/doc/build/changelog/unreleased_13/5269.rst
@@ -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`.
+
diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py
index 7d33c4649..6ac56a324 100644
--- a/lib/sqlalchemy/orm/relationships.py
+++ b/lib/sqlalchemy/orm/relationships.py
@@ -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:
diff --git a/test/orm/test_query.py b/test/orm/test_query.py
index a58148108..ce9a6e3c5 100644
--- a/test/orm/test_query.py
+++ b/test/orm/test_query.py
@@ -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 = (