summaryrefslogtreecommitdiff
path: root/test/orm/inheritance/test_single.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2021-09-04 12:12:46 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2021-09-28 13:27:54 -0400
commit6ce0d644db60ce6ea89eb15a76e078c4fa1a9066 (patch)
treef800bc5e1c308eae078e732c834d8eca501461cb /test/orm/inheritance/test_single.py
parent52e8545b2df312898d46f6a5b119675e8d0aa956 (diff)
downloadsqlalchemy-6ce0d644db60ce6ea89eb15a76e078c4fa1a9066.tar.gz
warn or deprecate for auto-aliasing in joins
An extra layer of warning messages has been added to the functionality of :meth:`_orm.Query.join` and the ORM version of :meth:`_sql.Select.join`, where a few places where "automatic aliasing" continues to occur will now be called out as a pattern to avoid, mostly specific to the area of joined table inheritance where classes that share common base tables are being joined together without using explicit aliases. One case emits a legacy warning for a pattern that's not recommended, the other case is fully deprecated. The automatic aliasing within ORM join() which occurs for overlapping mapped tables does not work consistently with all APIs such as ``contains_eager()``, and rather than continue to try to make these use cases work everywhere, replacing with a more user-explicit pattern is clearer, less prone to bugs and simplifies SQLAlchemy's internals further. The warnings include links to the errors.rst page where each pattern is demonstrated along with the recommended pattern to fix. * Improved the exception message generated when configuring a mapping with joined table inheritance where the two tables either have no foreign key relationships set up, or where they have multiple foreign key relationships set up. The message is now ORM specific and includes context that the :paramref:`_orm.Mapper.inherit_condition` parameter may be needed particularly for the ambiguous foreign keys case. * Add explicit support in the _expect_warnings() assertion for nested _expect_warnings calls * generalize the NoCache fixture, which we also need to catch warnings during compilation consistently * generalize the __str__() method for the HasCode mixin so all warnings and errors include the code link in their string Fixes: #6974 Change-Id: I84ed79ba2112c39eaab7973b6d6f46de7fa80842
Diffstat (limited to 'test/orm/inheritance/test_single.py')
-rw-r--r--test/orm/inheritance/test_single.py82
1 files changed, 54 insertions, 28 deletions
diff --git a/test/orm/inheritance/test_single.py b/test/orm/inheritance/test_single.py
index 32cc57020..1976dab06 100644
--- a/test/orm/inheritance/test_single.py
+++ b/test/orm/inheritance/test_single.py
@@ -10,6 +10,7 @@ from sqlalchemy import select
from sqlalchemy import String
from sqlalchemy import testing
from sqlalchemy import true
+from sqlalchemy import util
from sqlalchemy.orm import aliased
from sqlalchemy.orm import Bundle
from sqlalchemy.orm import joinedload
@@ -28,6 +29,13 @@ from sqlalchemy.testing.schema import Column
from sqlalchemy.testing.schema import Table
+def _aliased_join_warning(arg):
+ return testing.expect_warnings(
+ "An alias is being generated automatically against joined entity "
+ "mapped class %s due to overlapping tables" % (arg,)
+ )
+
+
class SingleInheritanceTest(testing.AssertsCompiledSQL, fixtures.MappedTest):
__dialect__ = "default"
@@ -1772,24 +1780,32 @@ class SingleFromPolySelectableTest(
"AS anon_1 WHERE anon_1.employee_type IN ([POSTCOMPILE_type_1])",
)
- def test_single_inh_subclass_join_joined_inh_subclass(self):
+ @testing.combinations((True,), (False,), argnames="autoalias")
+ def test_single_inh_subclass_join_joined_inh_subclass(self, autoalias):
Boss, Engineer = self.classes("Boss", "Engineer")
s = fixture_session()
- q = s.query(Boss).join(Engineer, Engineer.manager_id == Boss.id)
+ if autoalias:
+ q = s.query(Boss).join(Engineer, Engineer.manager_id == Boss.id)
+ else:
+ e1 = aliased(Engineer, flat=True)
+ q = s.query(Boss).join(e1, e1.manager_id == Boss.id)
- self.assert_compile(
- q,
- "SELECT manager.id AS manager_id, employee.id AS employee_id, "
- "employee.name AS employee_name, "
- "employee.type AS employee_type, "
- "manager.manager_data AS manager_manager_data "
- "FROM employee JOIN manager ON employee.id = manager.id "
- "JOIN (employee AS employee_1 JOIN engineer AS engineer_1 "
- "ON employee_1.id = engineer_1.id) "
- "ON engineer_1.manager_id = manager.id "
- "WHERE employee.type IN ([POSTCOMPILE_type_1])",
- )
+ with _aliased_join_warning(
+ "Engineer->engineer"
+ ) if autoalias else util.nullcontext():
+ self.assert_compile(
+ q,
+ "SELECT manager.id AS manager_id, employee.id AS employee_id, "
+ "employee.name AS employee_name, "
+ "employee.type AS employee_type, "
+ "manager.manager_data AS manager_manager_data "
+ "FROM employee JOIN manager ON employee.id = manager.id "
+ "JOIN (employee AS employee_1 JOIN engineer AS engineer_1 "
+ "ON employee_1.id = engineer_1.id) "
+ "ON engineer_1.manager_id = manager.id "
+ "WHERE employee.type IN ([POSTCOMPILE_type_1])",
+ )
def test_single_inh_subclass_join_wpoly_joined_inh_subclass(self):
Boss = self.classes.Boss
@@ -1828,25 +1844,35 @@ class SingleFromPolySelectableTest(
"WHERE employee.type IN ([POSTCOMPILE_type_1])",
)
- def test_joined_inh_subclass_join_single_inh_subclass(self):
+ @testing.combinations((True,), (False,), argnames="autoalias")
+ def test_joined_inh_subclass_join_single_inh_subclass(self, autoalias):
Engineer = self.classes.Engineer
Boss = self.classes.Boss
s = fixture_session()
- q = s.query(Engineer).join(Boss, Engineer.manager_id == Boss.id)
+ if autoalias:
+ q = s.query(Engineer).join(Boss, Engineer.manager_id == Boss.id)
+ else:
+ b1 = aliased(Boss, flat=True)
+ q = s.query(Engineer).join(b1, Engineer.manager_id == b1.id)
- self.assert_compile(
- q,
- "SELECT engineer.id AS engineer_id, employee.id AS employee_id, "
- "employee.name AS employee_name, employee.type AS employee_type, "
- "engineer.engineer_info AS engineer_engineer_info, "
- "engineer.manager_id AS engineer_manager_id "
- "FROM employee JOIN engineer ON employee.id = engineer.id "
- "JOIN (employee AS employee_1 JOIN manager AS manager_1 "
- "ON employee_1.id = manager_1.id) "
- "ON engineer.manager_id = manager_1.id "
- "AND employee_1.type IN ([POSTCOMPILE_type_1])",
- )
+ with _aliased_join_warning(
+ "Boss->manager"
+ ) if autoalias else util.nullcontext():
+ self.assert_compile(
+ q,
+ "SELECT engineer.id AS engineer_id, "
+ "employee.id AS employee_id, "
+ "employee.name AS employee_name, "
+ "employee.type AS employee_type, "
+ "engineer.engineer_info AS engineer_engineer_info, "
+ "engineer.manager_id AS engineer_manager_id "
+ "FROM employee JOIN engineer ON employee.id = engineer.id "
+ "JOIN (employee AS employee_1 JOIN manager AS manager_1 "
+ "ON employee_1.id = manager_1.id) "
+ "ON engineer.manager_id = manager_1.id "
+ "AND employee_1.type IN ([POSTCOMPILE_type_1])",
+ )
class EagerDefaultEvalTest(fixtures.DeclarativeMappedTest):