diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2021-09-04 12:12:46 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2021-09-28 13:27:54 -0400 |
commit | 6ce0d644db60ce6ea89eb15a76e078c4fa1a9066 (patch) | |
tree | f800bc5e1c308eae078e732c834d8eca501461cb /test/orm/inheritance/test_single.py | |
parent | 52e8545b2df312898d46f6a5b119675e8d0aa956 (diff) | |
download | sqlalchemy-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.py | 82 |
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): |