]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Provide a more detailed error message for Query.join()
authorRamonWill <ramonwilliams@hotmail.co.uk>
Tue, 25 Aug 2020 00:14:15 +0000 (20:14 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 31 Aug 2020 23:01:15 +0000 (19:01 -0400)
An :class:`.ArgumentError` with more detail is now raised if the target
parameter for :meth:`_query.Query.join` is set to an unmapped object.
Prior to this change a less detailed ``AttributeError`` was raised.
Pull request courtesy Ramon Williams.

Fixes: #4428
Closes: #5452
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/5452
Pull-request-sha: b148df547037e9a254fe331eff8e922c78426261

Change-Id: I873453d1fdb651178216aac698baac63ae5a94e8

doc/build/changelog/unreleased_13/4428.rst [new file with mode: 0644]
lib/sqlalchemy/orm/context.py
test/orm/test_joins.py

diff --git a/doc/build/changelog/unreleased_13/4428.rst b/doc/build/changelog/unreleased_13/4428.rst
new file mode 100644 (file)
index 0000000..e677669
--- /dev/null
@@ -0,0 +1,8 @@
+.. change::
+    :tags: bug, orm
+    :tickets: 4428
+
+    An :class:`.ArgumentError` with more detail is now raised if the target
+    parameter for :meth:`_query.Query.join` is set to an unmapped object.
+    Prior to this change a less detailed ``AttributeError`` was raised.
+    Pull request courtesy Ramon Williams.
index 0868fb29b4f33ea716c4592fd83270d820a64ced..d9e334d45adaf2eb35c25b6c6b01d782880bf686 100644 (file)
@@ -1170,7 +1170,18 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
                     if of_type:
                         right = of_type
                     else:
-                        right = onclause.property.entity
+                        right = onclause.property
+
+                        try:
+                            right = right.entity
+                        except AttributeError as err:
+                            util.raise_(
+                                sa_exc.ArgumentError(
+                                    "Join target %s does not refer to a "
+                                    "mapped entity" % right
+                                ),
+                                replace_context=err,
+                            )
 
                 left = onclause._parententity
 
@@ -1312,7 +1323,18 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
                     if of_type:
                         right = of_type
                     else:
-                        right = onclause.property.entity
+                        right = onclause.property
+
+                        try:
+                            right = right.entity
+                        except AttributeError as err:
+                            util.raise_(
+                                sa_exc.ArgumentError(
+                                    "Join target %s does not refer to a "
+                                    "mapped entity" % right
+                                ),
+                                replace_context=err,
+                            )
 
                 left = onclause._parententity
 
index 4ffa5fb9e9c37a7f5e7e6eafc5741dc9489f37ed..8225214f63451eac182b39bd7d2762a5ebba2bec 100644 (file)
@@ -2220,17 +2220,55 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
             ._compile_context,
         )
 
-    def test_on_clause_no_right_side(self):
+    def test_on_clause_no_right_side_one(self):
         User = self.classes.User
         Address = self.classes.Address
         sess = create_session()
 
+        # coercions does not catch this due to the
+        # legacy=True flag for JoinTargetRole
         assert_raises_message(
             sa_exc.ArgumentError,
             "Expected mapped entity or selectable/table as join target",
             sess.query(User).join(User.id == Address.user_id)._compile_context,
         )
 
+    def test_on_clause_no_right_side_one_future(self):
+        User = self.classes.User
+        Address = self.classes.Address
+
+        # future mode can raise a more specific error at the coercions level
+        assert_raises_message(
+            sa_exc.ArgumentError,
+            "Join target, typically a FROM expression, "
+            "or ORM relationship attribute expected",
+            select(User).join,
+            User.id == Address.user_id,
+        )
+
+    def test_on_clause_no_right_side_two(self):
+        User = self.classes.User
+        Address = self.classes.Address
+        sess = create_session()
+
+        assert_raises_message(
+            sa_exc.ArgumentError,
+            "Join target Address.user_id does not refer to a mapped entity",
+            sess.query(User).join(Address.user_id)._compile_context,
+        )
+
+    def test_on_clause_no_right_side_two_future(self):
+        User = self.classes.User
+        Address = self.classes.Address
+
+        stmt = select(User).join(Address.user_id)
+
+        assert_raises_message(
+            sa_exc.ArgumentError,
+            "Join target Address.user_id does not refer to a mapped entity",
+            stmt.compile,
+        )
+
     def test_select_from(self):
         """Test that the left edge of the join can be set reliably with
         select_from()."""