]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
deprecation warnings: with_parent, aliased, from_joinpoint
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 26 Oct 2021 19:37:16 +0000 (15:37 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 27 Oct 2021 15:41:00 +0000 (11:41 -0400)
most of the work for aliased / from_joinpoint has been done
already as I added all new tests for these and moved
most aliased/from_joinpoint to test/orm/test_deprecations.py
already

Change-Id: Ia23e332dec183de17b2fb9d89d946af8d5e89ae7

16 files changed:
lib/sqlalchemy/exc.py
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/testing/assertions.py
lib/sqlalchemy/testing/warnings.py
lib/sqlalchemy/util/deprecations.py
test/base/test_except.py
test/orm/inheritance/test_deprecations.py [new file with mode: 0644]
test/orm/inheritance/test_polymorphic_rel.py
test/orm/inheritance/test_single.py
test/orm/test_cascade.py
test/orm/test_deprecations.py
test/orm/test_lambdas.py
test/orm/test_lazy_relations.py
test/orm/test_naturalpks.py
test/orm/test_query.py
test/orm/test_relationships.py

index 9b9c82acaa9f8001b3af15a894339885b52e706d..7fa77120c6577533cda7210cfa36c5508dd9e897 100644 (file)
@@ -685,8 +685,9 @@ class SADeprecationWarning(HasDescriptionCode, DeprecationWarning):
     "Indicates the version that started raising this deprecation warning"
 
 
-class RemovedIn20Warning(SADeprecationWarning):
-    """Issued for usage of APIs specifically deprecated in SQLAlchemy 2.0.
+class Base20DeprecationWarning(SADeprecationWarning):
+    """Issued for usage of APIs specifically deprecated or legacy in
+    SQLAlchemy 2.0.
 
     .. seealso::
 
@@ -701,11 +702,19 @@ class RemovedIn20Warning(SADeprecationWarning):
 
     def __str__(self):
         return (
-            super(RemovedIn20Warning, self).__str__()
+            super(Base20DeprecationWarning, self).__str__()
             + " (Background on SQLAlchemy 2.0 at: https://sqlalche.me/e/b8d9)"
         )
 
 
+class LegacyAPIWarning(Base20DeprecationWarning):
+    """indicates an API that is in 'legacy' status, a long term deprecation."""
+
+
+class RemovedIn20Warning(Base20DeprecationWarning):
+    """indicates an API that will be fully removed in SQLAlchemy 2.0."""
+
+
 class MovedIn20Warning(RemovedIn20Warning):
     """Subtype of RemovedIn20Warning to indicate an API that moved only."""
 
index d48e34923ad6f162dd8dc20896710d952e19e900..eddb9c29c16b0fc1d61769e68185aa13e6fe218b 100644 (file)
@@ -1711,6 +1711,7 @@ class Query(
         we will attempt to derive an expression from based on string name.
 
         """
+
         if self._legacy_setup_joins:
             _last_joined_entity = self._last_joined_entity
             if _last_joined_entity is not None:
index 986dbb5e9b42da3c4a1581b89fc38495dee7199f..6bf14aecde9cdb1565db081bfa2922588825273a 100644 (file)
@@ -86,7 +86,7 @@ def expect_deprecated(*messages, **kw):
 
 
 def expect_deprecated_20(*messages, **kw):
-    return _expect_warnings(sa_exc.RemovedIn20Warning, messages, **kw)
+    return _expect_warnings(sa_exc.Base20DeprecationWarning, messages, **kw)
 
 
 def emits_warning_on(db, *messages):
index 40d03f0afee0904e30ece030749845753ceca415..f29d6569218d46e8565b1b00bb97de9bfc1e65e5 100644 (file)
@@ -69,9 +69,6 @@ def setup_filters():
         # ORM Query
         #
         r"The Query\.get\(\) method",
-        r"The Query\.with_parent\(\) method",
-        r"The Query\.with_parent\(\) method",
-        r"The ``aliased`` and ``from_joinpoint`` keyword arguments",
         r"The Query.with_polymorphic\(\) method is considered "
         "legacy as of the 1.x series",
         #
index c1acde39beddefa1f9b8980c0a3309b6105d814c..e1138aaefc618f03512994e99268604a73f47eb2 100644 (file)
@@ -98,13 +98,18 @@ def deprecated_20_cls(
     if alternative:
         message += " " + alternative
 
+    if becomes_legacy:
+        warning_cls = exc.LegacyAPIWarning
+    else:
+        warning_cls = exc.RemovedIn20Warning
+
     def decorate(cls):
         return _decorate_cls_with_warning(
             cls,
             constructor,
-            exc.RemovedIn20Warning,
+            warning_cls,
             message,
-            exc.RemovedIn20Warning.deprecated_since,
+            warning_cls.deprecated_since,
             message,
         )
 
index be6f448bd84fec5b4cb57a4ce95ca2609ad1ccf7..767fd233c01a76e2560576b3a8e13090b2bf10e7 100644 (file)
@@ -507,6 +507,8 @@ ALL_EXC = [
     (
         [
             sa_exceptions.SADeprecationWarning,
+            sa_exceptions.Base20DeprecationWarning,
+            sa_exceptions.LegacyAPIWarning,
             sa_exceptions.RemovedIn20Warning,
             sa_exceptions.MovedIn20Warning,
             sa_exceptions.SAWarning,
diff --git a/test/orm/inheritance/test_deprecations.py b/test/orm/inheritance/test_deprecations.py
new file mode 100644 (file)
index 0000000..bf8219c
--- /dev/null
@@ -0,0 +1,260 @@
+from sqlalchemy import ForeignKey
+from sqlalchemy import Integer
+from sqlalchemy import String
+from sqlalchemy import testing
+from sqlalchemy.orm import relationship
+from sqlalchemy.testing import eq_
+from sqlalchemy.testing import fixtures
+from sqlalchemy.testing.assertions import expect_deprecated_20
+from sqlalchemy.testing.fixtures import fixture_session
+from sqlalchemy.testing.schema import Column
+from sqlalchemy.testing.schema import Table
+from ._poly_fixtures import _Polymorphic
+from ._poly_fixtures import _PolymorphicAliasedJoins
+from ._poly_fixtures import _PolymorphicJoins
+from ._poly_fixtures import _PolymorphicPolymorphic
+from ._poly_fixtures import _PolymorphicUnions
+from ._poly_fixtures import Company
+from ._poly_fixtures import Engineer
+from ._poly_fixtures import Manager
+from ._poly_fixtures import Paperwork
+from ._poly_fixtures import Person
+
+
+aliased_jp_dep = (
+    r"The ``aliased`` and ``from_joinpoint`` keyword arguments "
+    r"to Query.join\(\) are deprecated"
+)
+
+
+class _PolymorphicTestBase(fixtures.NoCache):
+    __backend__ = True
+    __dialect__ = "default_enhanced"
+
+    @classmethod
+    def setup_mappers(cls):
+        super(_PolymorphicTestBase, cls).setup_mappers()
+        global people, engineers, managers, boss
+        global companies, paperwork, machines
+        people, engineers, managers, boss, companies, paperwork, machines = (
+            cls.tables.people,
+            cls.tables.engineers,
+            cls.tables.managers,
+            cls.tables.boss,
+            cls.tables.companies,
+            cls.tables.paperwork,
+            cls.tables.machines,
+        )
+
+    @classmethod
+    def insert_data(cls, connection):
+        super(_PolymorphicTestBase, cls).insert_data(connection)
+
+        global all_employees, c1_employees, c2_employees
+        global c1, c2, e1, e2, e3, b1, m1
+        c1, c2, all_employees, c1_employees, c2_employees = (
+            cls.c1,
+            cls.c2,
+            cls.all_employees,
+            cls.c1_employees,
+            cls.c2_employees,
+        )
+        e1, e2, e3, b1, m1 = cls.e1, cls.e2, cls.e3, cls.b1, cls.m1
+
+    def test_join_from_polymorphic_flag_aliased_one(self):
+        sess = fixture_session()
+        with expect_deprecated_20(aliased_jp_dep):
+            eq_(
+                sess.query(Person)
+                .order_by(Person.person_id)
+                .join(Person.paperwork, aliased=True)
+                .filter(Paperwork.description.like("%review%"))
+                .all(),
+                [b1, m1],
+            )
+
+    def test_join_from_polymorphic_flag_aliased_two(self):
+        sess = fixture_session()
+        with expect_deprecated_20(aliased_jp_dep):
+            eq_(
+                sess.query(Person)
+                .order_by(Person.person_id)
+                .join(Person.paperwork, aliased=True)
+                .filter(Paperwork.description.like("%#2%"))
+                .all(),
+                [e1, m1],
+            )
+
+    def test_join_from_with_polymorphic_flag_aliased_one(self):
+        sess = fixture_session()
+        with expect_deprecated_20(aliased_jp_dep):
+            eq_(
+                sess.query(Person)
+                .with_polymorphic(Manager)
+                .join(Person.paperwork, aliased=True)
+                .filter(Paperwork.description.like("%review%"))
+                .all(),
+                [b1, m1],
+            )
+
+    def test_join_from_with_polymorphic_flag_aliased_two(self):
+        sess = fixture_session()
+        with expect_deprecated_20(aliased_jp_dep):
+            eq_(
+                sess.query(Person)
+                .with_polymorphic([Manager, Engineer])
+                .order_by(Person.person_id)
+                .join(Person.paperwork, aliased=True)
+                .filter(Paperwork.description.like("%#2%"))
+                .all(),
+                [e1, m1],
+            )
+
+    def test_join_to_polymorphic_flag_aliased(self):
+        sess = fixture_session()
+        with expect_deprecated_20(aliased_jp_dep):
+            eq_(
+                sess.query(Company)
+                .join(Company.employees, aliased=True)
+                .filter(Person.name == "vlad")
+                .one(),
+                c2,
+            )
+
+    def test_polymorphic_any_flag_alias_two(self):
+        sess = fixture_session()
+        # test that the aliasing on "Person" does not bleed into the
+        # EXISTS clause generated by any()
+        any_ = Company.employees.any(Person.name == "wally")
+        with expect_deprecated_20(aliased_jp_dep):
+            eq_(
+                sess.query(Company)
+                .join(Company.employees, aliased=True)
+                .filter(Person.name == "dilbert")
+                .filter(any_)
+                .all(),
+                [c1],
+            )
+
+    def test_join_from_polymorphic_flag_aliased_three(self):
+        sess = fixture_session()
+        with expect_deprecated_20(aliased_jp_dep):
+            eq_(
+                sess.query(Engineer)
+                .order_by(Person.person_id)
+                .join(Person.paperwork, aliased=True)
+                .filter(Paperwork.description.like("%#2%"))
+                .all(),
+                [e1],
+            )
+
+
+class PolymorphicTest(_PolymorphicTestBase, _Polymorphic):
+    pass
+
+
+class PolymorphicPolymorphicTest(
+    _PolymorphicTestBase, _PolymorphicPolymorphic
+):
+    pass
+
+
+class PolymorphicUnionsTest(_PolymorphicTestBase, _PolymorphicUnions):
+    pass
+
+
+class PolymorphicAliasedJoinsTest(
+    _PolymorphicTestBase, _PolymorphicAliasedJoins
+):
+    pass
+
+
+class PolymorphicJoinsTest(_PolymorphicTestBase, _PolymorphicJoins):
+    pass
+
+
+class RelationshipToSingleTest(
+    testing.AssertsCompiledSQL, fixtures.MappedTest
+):
+    __dialect__ = "default"
+
+    @classmethod
+    def define_tables(cls, metadata):
+        Table(
+            "employees",
+            metadata,
+            Column(
+                "employee_id",
+                Integer,
+                primary_key=True,
+                test_needs_autoincrement=True,
+            ),
+            Column("name", String(50)),
+            Column("manager_data", String(50)),
+            Column("engineer_info", String(50)),
+            Column("type", String(20)),
+            Column("company_id", Integer, ForeignKey("companies.company_id")),
+        )
+
+        Table(
+            "companies",
+            metadata,
+            Column(
+                "company_id",
+                Integer,
+                primary_key=True,
+                test_needs_autoincrement=True,
+            ),
+            Column("name", String(50)),
+        )
+
+    @classmethod
+    def setup_classes(cls):
+        class Company(cls.Comparable):
+            pass
+
+        class Employee(cls.Comparable):
+            pass
+
+        class Manager(Employee):
+            pass
+
+        class Engineer(Employee):
+            pass
+
+        class JuniorEngineer(Engineer):
+            pass
+
+    def test_of_type_aliased_fromjoinpoint(self):
+        Company, Employee, Engineer = (
+            self.classes.Company,
+            self.classes.Employee,
+            self.classes.Engineer,
+        )
+        companies, employees = self.tables.companies, self.tables.employees
+
+        self.mapper_registry.map_imperatively(
+            Company, companies, properties={"employee": relationship(Employee)}
+        )
+        self.mapper_registry.map_imperatively(
+            Employee, employees, polymorphic_on=employees.c.type
+        )
+        self.mapper_registry.map_imperatively(
+            Engineer, inherits=Employee, polymorphic_identity="engineer"
+        )
+
+        sess = fixture_session()
+
+        with expect_deprecated_20(aliased_jp_dep):
+            self.assert_compile(
+                sess.query(Company).outerjoin(
+                    Company.employee.of_type(Engineer),
+                    aliased=True,
+                    from_joinpoint=True,
+                ),
+                "SELECT companies.company_id AS companies_company_id, "
+                "companies.name AS companies_name FROM companies "
+                "LEFT OUTER JOIN employees AS employees_1 ON "
+                "companies.company_id = employees_1.company_id "
+                "AND employees_1.type IN ([POSTCOMPILE_type_1])",
+            )
index b9d9ad932b438addf1dcecdb9a46a8781f7e68db..2e52e162bbb7826fad690221a2a851ad79c103e6 100644 (file)
@@ -386,18 +386,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
             [m1],
         )
 
-    def test_join_from_polymorphic_flag_aliased_one(self):
-        sess = fixture_session()
-        eq_(
-            sess.query(Person)
-            .order_by(Person.person_id)
-            .join(Person.paperwork, aliased=True)
-            .filter(Paperwork.description.like("%review%"))
-            .all(),
-            [b1, m1],
-        )
-
-    def test_join_from_polymorphic_flag_aliased_one_future(self):
+    def test_join_from_polymorphic_aliased_one_future(self):
         sess = fixture_session(future=True)
 
         pa = aliased(Paperwork)
@@ -426,17 +415,6 @@ class _PolymorphicTestBase(fixtures.NoCache):
             [b1, m1],
         )
 
-    def test_join_from_polymorphic_flag_aliased_two(self):
-        sess = fixture_session()
-        eq_(
-            sess.query(Person)
-            .order_by(Person.person_id)
-            .join(Person.paperwork, aliased=True)
-            .filter(Paperwork.description.like("%#2%"))
-            .all(),
-            [e1, m1],
-        )
-
     def test_join_from_polymorphic_explicit_aliased_two(self):
         sess = fixture_session()
         pa = aliased(Paperwork)
@@ -449,17 +427,6 @@ class _PolymorphicTestBase(fixtures.NoCache):
             [e1, m1],
         )
 
-    def test_join_from_polymorphic_flag_aliased_three(self):
-        sess = fixture_session()
-        eq_(
-            sess.query(Engineer)
-            .order_by(Person.person_id)
-            .join(Person.paperwork, aliased=True)
-            .filter(Paperwork.description.like("%#2%"))
-            .all(),
-            [e1],
-        )
-
     def test_join_from_polymorphic_explicit_aliased_three(self):
         sess = fixture_session()
         pa = aliased(Paperwork)
@@ -539,17 +506,6 @@ class _PolymorphicTestBase(fixtures.NoCache):
             [m1],
         )
 
-    def test_join_from_with_polymorphic_flag_aliased_one(self):
-        sess = fixture_session()
-        eq_(
-            sess.query(Person)
-            .with_polymorphic(Manager)
-            .join(Person.paperwork, aliased=True)
-            .filter(Paperwork.description.like("%review%"))
-            .all(),
-            [b1, m1],
-        )
-
     def test_join_from_with_polymorphic_explicit_aliased_one(self):
         sess = fixture_session()
         pa = aliased(Paperwork)
@@ -562,18 +518,6 @@ class _PolymorphicTestBase(fixtures.NoCache):
             [b1, m1],
         )
 
-    def test_join_from_with_polymorphic_flag_aliased_two(self):
-        sess = fixture_session()
-        eq_(
-            sess.query(Person)
-            .with_polymorphic([Manager, Engineer])
-            .order_by(Person.person_id)
-            .join(Person.paperwork, aliased=True)
-            .filter(Paperwork.description.like("%#2%"))
-            .all(),
-            [e1, m1],
-        )
-
     def test_join_from_with_polymorphic_explicit_aliased_two(self):
         sess = fixture_session()
         pa = aliased(Paperwork)
@@ -612,16 +556,6 @@ class _PolymorphicTestBase(fixtures.NoCache):
             c2,
         )
 
-    def test_join_to_polymorphic_flag_aliased(self):
-        sess = fixture_session()
-        eq_(
-            sess.query(Company)
-            .join(Company.employees, aliased=True)
-            .filter(Person.name == "vlad")
-            .one(),
-            c2,
-        )
-
     def test_join_to_polymorphic_explicit_aliased(self):
         sess = fixture_session()
         ea = aliased(Person)
@@ -639,20 +573,6 @@ class _PolymorphicTestBase(fixtures.NoCache):
         any_ = Company.employees.any(Person.name == "vlad")
         eq_(sess.query(Company).filter(any_).all(), [c2])
 
-    def test_polymorphic_any_flag_alias_two(self):
-        sess = fixture_session()
-        # test that the aliasing on "Person" does not bleed into the
-        # EXISTS clause generated by any()
-        any_ = Company.employees.any(Person.name == "wally")
-        eq_(
-            sess.query(Company)
-            .join(Company.employees, aliased=True)
-            .filter(Person.name == "dilbert")
-            .filter(any_)
-            .all(),
-            [c1],
-        )
-
     def test_polymorphic_any_explicit_alias_two(self):
         sess = fixture_session()
         # test that the aliasing on "Person" does not bleed into the
index 066f781d88aea5f531ed1432e211cafea7b87b35..025f717d7d81c6008413dae55a0ac211be4c9a41 100644 (file)
@@ -922,38 +922,6 @@ class RelationshipToSingleTest(
             [Company(name="c1")],
         )
 
-    def test_of_type_aliased_fromjoinpoint(self):
-        Company, Employee, Engineer = (
-            self.classes.Company,
-            self.classes.Employee,
-            self.classes.Engineer,
-        )
-        companies, employees = self.tables.companies, self.tables.employees
-
-        self.mapper_registry.map_imperatively(
-            Company, companies, properties={"employee": relationship(Employee)}
-        )
-        self.mapper_registry.map_imperatively(
-            Employee, employees, polymorphic_on=employees.c.type
-        )
-        self.mapper_registry.map_imperatively(
-            Engineer, inherits=Employee, polymorphic_identity="engineer"
-        )
-
-        sess = fixture_session()
-        self.assert_compile(
-            sess.query(Company).outerjoin(
-                Company.employee.of_type(Engineer),
-                aliased=True,
-                from_joinpoint=True,
-            ),
-            "SELECT companies.company_id AS companies_company_id, "
-            "companies.name AS companies_name FROM companies "
-            "LEFT OUTER JOIN employees AS employees_1 ON "
-            "companies.company_id = employees_1.company_id "
-            "AND employees_1.type IN ([POSTCOMPILE_type_1])",
-        )
-
     def test_join_explicit_onclause_no_discriminator(self):
         # test issue #3462
         Company, Employee, Engineer = (
index 3e42368df2ddbb1f825b6f4699736c281da3cef8..8749a0147363213c99c0cd542088bfb4335f5ac7 100644 (file)
@@ -18,6 +18,7 @@ from sqlalchemy.orm import object_mapper
 from sqlalchemy.orm import relationship
 from sqlalchemy.orm import Session
 from sqlalchemy.orm import util as orm_util
+from sqlalchemy.orm import with_parent
 from sqlalchemy.orm.attributes import instance_state
 from sqlalchemy.orm.collections import attribute_mapped_collection
 from sqlalchemy.orm.decl_api import declarative_base
@@ -2002,7 +2003,7 @@ class M2OCascadeDeleteOrphanTestOne(fixtures.MappedTest):
         assert p1 not in sess
         sess.flush()
         eq_(
-            sess.query(Pref).with_parent(someuser).all(),
+            sess.query(Pref).filter(with_parent(someuser, User.pref)).all(),
             [Pref(data="someotherpref")],
         )
 
index c66259e4789e8f2cf81f2153b24c5b3932f26549..f6854cf2716c30a68c093da5d16e47518df76300 100644 (file)
@@ -134,6 +134,10 @@ wparent_strings_dep = (
     r"in the ORM with_parent\(\) function"
 )
 
+query_wparent_dep = (
+    r"The Query.with_parent\(\) method is considered legacy as of the 1.x"
+)
+
 sef_dep = (
     r"The Query.select_entity_from\(\) method is considered "
     "legacy as of the 1.x"
@@ -4714,6 +4718,27 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
         assert q.count() == 1
         assert [User(id=7)] == q.all()
 
+    def test_does_filter_aliasing_work(self):
+        User, Address = self.classes("User", "Address")
+
+        s = fixture_session()
+
+        # aliased=True is to be deprecated, other filter lambdas
+        # that go into effect include polymorphic filtering.
+        with testing.expect_deprecated(join_aliased_dep):
+            q = (
+                s.query(lambda: User)
+                .join(lambda: User.addresses, aliased=True)
+                .filter(lambda: Address.email_address == "foo")
+            )
+        self.assert_compile(
+            q,
+            "SELECT users.id AS users_id, users.name AS users_name "
+            "FROM users JOIN addresses AS addresses_1 "
+            "ON users.id = addresses_1.user_id "
+            "WHERE addresses_1.email_address = :email_address_1",
+        )
+
     def test_overlapping_paths_two(self):
         User = self.classes.User
 
@@ -8161,3 +8186,267 @@ class JoinLateralTest(fixtures.MappedTest, AssertsCompiledSQL):
             "generate_series(:generate_series_1, anon_1.bookcase_shelves) "
             "AS anon_2 ON true",
         )
+
+
+class ParentTest(QueryTest, AssertsCompiledSQL):
+    __dialect__ = "default"
+
+    def test_o2m(self):
+        User, orders, Order = (
+            self.classes.User,
+            self.tables.orders,
+            self.classes.Order,
+        )
+
+        sess = fixture_session()
+        q = sess.query(User)
+
+        u1 = q.filter_by(name="jack").one()
+
+        # test auto-lookup of property
+        with assertions.expect_deprecated_20(query_wparent_dep):
+            o = sess.query(Order).with_parent(u1).all()
+        assert [
+            Order(description="order 1"),
+            Order(description="order 3"),
+            Order(description="order 5"),
+        ] == o
+
+        # test with explicit property
+        with assertions.expect_deprecated_20(query_wparent_dep):
+            o = sess.query(Order).with_parent(u1, property=User.orders).all()
+        assert [
+            Order(description="order 1"),
+            Order(description="order 3"),
+            Order(description="order 5"),
+        ] == o
+
+        with assertions.expect_deprecated_20(query_wparent_dep):
+            # test generative criterion
+            o = sess.query(Order).with_parent(u1).filter(orders.c.id > 2).all()
+        assert [
+            Order(description="order 3"),
+            Order(description="order 5"),
+        ] == o
+
+    def test_select_from(self):
+        User, Address = self.classes.User, self.classes.Address
+
+        sess = fixture_session()
+        u1 = sess.query(User).get(7)
+        with assertions.expect_deprecated_20(query_wparent_dep):
+            q = sess.query(Address).select_from(Address).with_parent(u1)
+        self.assert_compile(
+            q,
+            "SELECT addresses.id AS addresses_id, "
+            "addresses.user_id AS addresses_user_id, "
+            "addresses.email_address AS addresses_email_address "
+            "FROM addresses WHERE :param_1 = addresses.user_id",
+            {"param_1": 7},
+        )
+
+    def test_from_entity_query_entity(self):
+        User, Address = self.classes.User, self.classes.Address
+
+        sess = fixture_session()
+        u1 = sess.query(User).get(7)
+        with assertions.expect_deprecated_20(query_wparent_dep):
+            q = sess.query(User, Address).with_parent(
+                u1, User.addresses, from_entity=Address
+            )
+        self.assert_compile(
+            q,
+            "SELECT users.id AS users_id, users.name AS users_name, "
+            "addresses.id AS addresses_id, addresses.user_id "
+            "AS addresses_user_id, "
+            "addresses.email_address AS addresses_email_address "
+            "FROM users, addresses "
+            "WHERE :param_1 = addresses.user_id",
+            {"param_1": 7},
+        )
+
+    def test_select_from_alias(self):
+        User, Address = self.classes.User, self.classes.Address
+
+        sess = fixture_session()
+        u1 = sess.query(User).get(7)
+        a1 = aliased(Address)
+        with assertions.expect_deprecated_20(query_wparent_dep):
+            q = sess.query(a1).with_parent(u1)
+        self.assert_compile(
+            q,
+            "SELECT addresses_1.id AS addresses_1_id, "
+            "addresses_1.user_id AS addresses_1_user_id, "
+            "addresses_1.email_address AS addresses_1_email_address "
+            "FROM addresses AS addresses_1 "
+            "WHERE :param_1 = addresses_1.user_id",
+            {"param_1": 7},
+        )
+
+    def test_select_from_alias_explicit_prop(self):
+        User, Address = self.classes.User, self.classes.Address
+
+        sess = fixture_session()
+        u1 = sess.query(User).get(7)
+        a1 = aliased(Address)
+        with assertions.expect_deprecated_20(query_wparent_dep):
+            q = sess.query(a1).with_parent(u1, User.addresses)
+        self.assert_compile(
+            q,
+            "SELECT addresses_1.id AS addresses_1_id, "
+            "addresses_1.user_id AS addresses_1_user_id, "
+            "addresses_1.email_address AS addresses_1_email_address "
+            "FROM addresses AS addresses_1 "
+            "WHERE :param_1 = addresses_1.user_id",
+            {"param_1": 7},
+        )
+
+    def test_select_from_alias_from_entity(self):
+        User, Address = self.classes.User, self.classes.Address
+
+        sess = fixture_session()
+        u1 = sess.query(User).get(7)
+        a1 = aliased(Address)
+        a2 = aliased(Address)
+        with assertions.expect_deprecated_20(query_wparent_dep):
+            q = sess.query(a1, a2).with_parent(
+                u1, User.addresses, from_entity=a2
+            )
+        self.assert_compile(
+            q,
+            "SELECT addresses_1.id AS addresses_1_id, "
+            "addresses_1.user_id AS addresses_1_user_id, "
+            "addresses_1.email_address AS addresses_1_email_address, "
+            "addresses_2.id AS addresses_2_id, "
+            "addresses_2.user_id AS addresses_2_user_id, "
+            "addresses_2.email_address AS addresses_2_email_address "
+            "FROM addresses AS addresses_1, "
+            "addresses AS addresses_2 WHERE :param_1 = addresses_2.user_id",
+            {"param_1": 7},
+        )
+
+    def test_select_from_alias_of_type(self):
+        User, Address = self.classes.User, self.classes.Address
+
+        sess = fixture_session()
+        u1 = sess.query(User).get(7)
+        a1 = aliased(Address)
+        a2 = aliased(Address)
+        with assertions.expect_deprecated_20(query_wparent_dep):
+            q = sess.query(a1, a2).with_parent(u1, User.addresses.of_type(a2))
+        self.assert_compile(
+            q,
+            "SELECT addresses_1.id AS addresses_1_id, "
+            "addresses_1.user_id AS addresses_1_user_id, "
+            "addresses_1.email_address AS addresses_1_email_address, "
+            "addresses_2.id AS addresses_2_id, "
+            "addresses_2.user_id AS addresses_2_user_id, "
+            "addresses_2.email_address AS addresses_2_email_address "
+            "FROM addresses AS addresses_1, "
+            "addresses AS addresses_2 WHERE :param_1 = addresses_2.user_id",
+            {"param_1": 7},
+        )
+
+    def test_noparent(self):
+        Item, User = self.classes.Item, self.classes.User
+
+        sess = fixture_session()
+        q = sess.query(User)
+
+        u1 = q.filter_by(name="jack").one()
+
+        with assertions.expect_deprecated_20(query_wparent_dep):
+            with assertions.expect_raises_message(
+                sa_exc.InvalidRequestError,
+                "Could not locate a property which relates "
+                "instances of class 'Item' to instances of class 'User'",
+            ):
+                q = sess.query(Item).with_parent(u1)
+
+    def test_m2m(self):
+        Item, Keyword = self.classes.Item, self.classes.Keyword
+
+        sess = fixture_session()
+        i1 = sess.query(Item).filter_by(id=2).one()
+        with assertions.expect_deprecated_20(query_wparent_dep):
+            k = sess.query(Keyword).with_parent(i1).all()
+        assert [
+            Keyword(name="red"),
+            Keyword(name="small"),
+            Keyword(name="square"),
+        ] == k
+
+    def test_with_transient(self):
+        User, Order = self.classes.User, self.classes.Order
+
+        sess = fixture_session()
+
+        q = sess.query(User)
+        u1 = q.filter_by(name="jack").one()
+        utrans = User(id=u1.id)
+        with assertions.expect_deprecated_20(query_wparent_dep):
+            o = sess.query(Order).with_parent(utrans, User.orders)
+        eq_(
+            [
+                Order(description="order 1"),
+                Order(description="order 3"),
+                Order(description="order 5"),
+            ],
+            o.all(),
+        )
+
+    def test_with_pending_autoflush(self):
+        Order, User = self.classes.Order, self.classes.User
+
+        sess = fixture_session()
+
+        o1 = sess.query(Order).first()
+        opending = Order(id=20, user_id=o1.user_id)
+        sess.add(opending)
+        with assertions.expect_deprecated_20(query_wparent_dep):
+            eq_(
+                sess.query(User).with_parent(opending, Order.user).one(),
+                User(id=o1.user_id),
+            )
+
+    def test_with_pending_no_autoflush(self):
+        Order, User = self.classes.Order, self.classes.User
+
+        sess = fixture_session(autoflush=False)
+
+        o1 = sess.query(Order).first()
+        opending = Order(user_id=o1.user_id)
+        sess.add(opending)
+        with assertions.expect_deprecated_20(query_wparent_dep):
+            eq_(
+                sess.query(User).with_parent(opending, Order.user).one(),
+                User(id=o1.user_id),
+            )
+
+    def test_unique_binds_union(self):
+        """bindparams used in the 'parent' query are unique"""
+        User, Address = self.classes.User, self.classes.Address
+
+        sess = fixture_session()
+        u1, u2 = sess.query(User).order_by(User.id)[0:2]
+
+        with assertions.expect_deprecated_20(query_wparent_dep):
+            q1 = sess.query(Address).with_parent(u1, User.addresses)
+        with assertions.expect_deprecated_20(query_wparent_dep):
+            q2 = sess.query(Address).with_parent(u2, User.addresses)
+
+        self.assert_compile(
+            q1.union(q2),
+            "SELECT anon_1.addresses_id AS anon_1_addresses_id, "
+            "anon_1.addresses_user_id AS anon_1_addresses_user_id, "
+            "anon_1.addresses_email_address AS "
+            "anon_1_addresses_email_address FROM (SELECT addresses.id AS "
+            "addresses_id, addresses.user_id AS addresses_user_id, "
+            "addresses.email_address AS addresses_email_address FROM "
+            "addresses WHERE :param_1 = addresses.user_id UNION SELECT "
+            "addresses.id AS addresses_id, addresses.user_id AS "
+            "addresses_user_id, addresses.email_address "
+            "AS addresses_email_address "
+            "FROM addresses WHERE :param_2 = addresses.user_id) AS anon_1",
+            checkparams={"param_1": 7, "param_2": 8},
+        )
index 30048585bc5514a5bd20541e26bdb65174bc8553..5274271d9facc679f1b7e444320a18026f8b11eb 100644 (file)
@@ -305,26 +305,6 @@ class LambdaTest(QueryTest, AssertsCompiledSQL):
             ):
                 self.assert_sql_count(testing.db, fn, 2)
 
-    def test_does_filter_aliasing_work(self, plain_fixture):
-        User, Address = plain_fixture
-
-        s = Session(testing.db, future=True)
-
-        # aliased=True is to be deprecated, other filter lambdas
-        # that go into effect include polymorphic filtering.
-        q = (
-            s.query(lambda: User)
-            .join(lambda: User.addresses, aliased=True)
-            .filter(lambda: Address.email_address == "foo")
-        )
-        self.assert_compile(
-            q,
-            "SELECT users.id AS users_id, users.name AS users_name "
-            "FROM users JOIN addresses AS addresses_1 "
-            "ON users.id = addresses_1.user_id "
-            "WHERE addresses_1.email_address = :email_address_1",
-        )
-
     @testing.combinations(
         lambda s, User, Address: s.query(lambda: User).join(lambda: Address),
         lambda s, User, Address: s.query(lambda: User).join(
index 2bea9b110675f185e03a8479d951fb890f7855d5..412637ec6a519e6c4f6a8f1ccbdf1b70d5ddd795 100644 (file)
@@ -23,6 +23,7 @@ from sqlalchemy.orm import configure_mappers
 from sqlalchemy.orm import exc as orm_exc
 from sqlalchemy.orm import relationship
 from sqlalchemy.orm import Session
+from sqlalchemy.orm import with_parent
 from sqlalchemy.testing import assert_raises
 from sqlalchemy.testing import eq_
 from sqlalchemy.testing import fixtures
@@ -658,14 +659,14 @@ class LazyTest(_fixtures.FixtureTest):
             [Order(id=1), Order(id=5)],
             fixture_session()
             .query(closed_mapper)
-            .with_parent(user, property=User.closed_orders)
+            .filter(with_parent(user, User.closed_orders))
             .all(),
         )
         eq_(
             [Order(id=3)],
             fixture_session()
             .query(open_mapper)
-            .with_parent(user, property=User.open_orders)
+            .filter(with_parent(user, User.open_orders))
             .all(),
         )
 
index 9f31c4b800602ad7d66fabe450f74e00e55fb256..fba335d285a131030a62af1027c40be0f54048c1 100644 (file)
@@ -12,8 +12,9 @@ from sqlalchemy import Integer
 from sqlalchemy import String
 from sqlalchemy import testing
 from sqlalchemy import TypeDecorator
+from sqlalchemy.orm import make_transient
 from sqlalchemy.orm import relationship
-from sqlalchemy.orm.session import make_transient
+from sqlalchemy.orm import with_parent
 from sqlalchemy.testing import assert_raises
 from sqlalchemy.testing import assert_raises_message
 from sqlalchemy.testing import eq_
@@ -621,7 +622,7 @@ class NaturalPKTest(fixtures.MappedTest):
         u2 = sess.query(User).get(u2.username)
         u2.username = "wendy"
         sess.flush()
-        r = sess.query(Item).with_parent(u2).all()
+        r = sess.query(Item).filter(with_parent(u2, User.items)).all()
         eq_(Item(itemname="item2"), r[0])
 
     def test_manytoone_deferred_relationship_expr(self):
index 1971964b1338b5b28784bdf895a60c6b3f83d7c2..a504a4eb1c3cf27cfd16adfb362631978a059386 100644 (file)
@@ -6352,9 +6352,8 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
     __dialect__ = "default"
 
     def test_o2m(self):
-        User, orders, Order = (
+        User, Order = (
             self.classes.User,
-            self.tables.orders,
             self.classes.Order,
         )
 
@@ -6363,22 +6362,6 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
 
         u1 = q.filter_by(name="jack").one()
 
-        # test auto-lookup of property
-        o = sess.query(Order).with_parent(u1).all()
-        assert [
-            Order(description="order 1"),
-            Order(description="order 3"),
-            Order(description="order 5"),
-        ] == o
-
-        # test with explicit property
-        o = sess.query(Order).with_parent(u1, property=User.orders).all()
-        assert [
-            Order(description="order 1"),
-            Order(description="order 3"),
-            Order(description="order 5"),
-        ] == o
-
         o = sess.query(Order).filter(with_parent(u1, User.orders)).all()
         assert [
             Order(description="order 1"),
@@ -6386,25 +6369,16 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
             Order(description="order 5"),
         ] == o
 
-        # test generative criterion
-        o = sess.query(Order).with_parent(u1).filter(orders.c.id > 2).all()
-        assert [
-            Order(description="order 3"),
-            Order(description="order 5"),
-        ] == o
-
-        # test against None for parent? this can't be done with the current
-        # API since we don't know what mapper to use
-        # assert
-        #     sess.query(Order).with_parent(None, property='addresses').all()
-        #     == [Order(description="order 5")]
-
     def test_select_from(self):
         User, Address = self.classes.User, self.classes.Address
 
         sess = fixture_session()
         u1 = sess.query(User).get(7)
-        q = sess.query(Address).select_from(Address).with_parent(u1)
+        q = (
+            sess.query(Address)
+            .select_from(Address)
+            .filter(with_parent(u1, User.addresses))
+        )
         self.assert_compile(
             q,
             "SELECT addresses.id AS addresses_id, "
@@ -6433,49 +6407,13 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
             {"param_1": 7},
         )
 
-    def test_from_entity_query_entity(self):
-        User, Address = self.classes.User, self.classes.Address
-
-        sess = fixture_session()
-        u1 = sess.query(User).get(7)
-        q = sess.query(User, Address).with_parent(
-            u1, User.addresses, from_entity=Address
-        )
-        self.assert_compile(
-            q,
-            "SELECT users.id AS users_id, users.name AS users_name, "
-            "addresses.id AS addresses_id, addresses.user_id "
-            "AS addresses_user_id, "
-            "addresses.email_address AS addresses_email_address "
-            "FROM users, addresses "
-            "WHERE :param_1 = addresses.user_id",
-            {"param_1": 7},
-        )
-
     def test_select_from_alias(self):
         User, Address = self.classes.User, self.classes.Address
 
         sess = fixture_session()
         u1 = sess.query(User).get(7)
         a1 = aliased(Address)
-        q = sess.query(a1).with_parent(u1)
-        self.assert_compile(
-            q,
-            "SELECT addresses_1.id AS addresses_1_id, "
-            "addresses_1.user_id AS addresses_1_user_id, "
-            "addresses_1.email_address AS addresses_1_email_address "
-            "FROM addresses AS addresses_1 "
-            "WHERE :param_1 = addresses_1.user_id",
-            {"param_1": 7},
-        )
-
-    def test_select_from_alias_explicit_prop(self):
-        User, Address = self.classes.User, self.classes.Address
-
-        sess = fixture_session()
-        u1 = sess.query(User).get(7)
-        a1 = aliased(Address)
-        q = sess.query(a1).with_parent(u1, User.addresses)
+        q = sess.query(a1).filter(with_parent(u1, User.addresses.of_type(a1)))
         self.assert_compile(
             q,
             "SELECT addresses_1.id AS addresses_1_id, "
@@ -6493,7 +6431,9 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
         u1 = sess.query(User).get(7)
         a1 = aliased(Address)
         a2 = aliased(Address)
-        q = sess.query(a1, a2).with_parent(u1, User.addresses, from_entity=a2)
+        q = sess.query(a1, a2).filter(
+            with_parent(u1, User.addresses, from_entity=a2)
+        )
         self.assert_compile(
             q,
             "SELECT addresses_1.id AS addresses_1_id, "
@@ -6514,7 +6454,9 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
         u1 = sess.query(User).get(7)
         a1 = aliased(Address)
         a2 = aliased(Address)
-        q = sess.query(a1, a2).with_parent(u1, User.addresses.of_type(a2))
+        q = sess.query(a1, a2).filter(
+            with_parent(u1, User.addresses.of_type(a2))
+        )
         self.assert_compile(
             q,
             "SELECT addresses_1.id AS addresses_1_id, "
@@ -6536,26 +6478,30 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
 
         u1 = q.filter_by(name="jack").one()
 
-        try:
-            q = sess.query(Item).with_parent(u1)
-            assert False
-        except sa_exc.InvalidRequestError as e:
-            assert (
-                str(e) == "Could not locate a property which relates "
-                "instances of class 'Item' to instances of class 'User'"
-            )
+        # TODO: this can perhaps raise an error, then again it's doing what's
+        # asked...
+        q = sess.query(Item).filter(with_parent(u1, User.orders))
+        self.assert_compile(
+            q,
+            "SELECT items.id AS items_id, "
+            "items.description AS items_description "
+            "FROM items, orders WHERE :param_1 = orders.user_id",
+        )
 
     def test_m2m(self):
         Item, Keyword = self.classes.Item, self.classes.Keyword
 
         sess = fixture_session()
         i1 = sess.query(Item).filter_by(id=2).one()
-        k = sess.query(Keyword).with_parent(i1).all()
-        assert [
-            Keyword(name="red"),
-            Keyword(name="small"),
-            Keyword(name="square"),
-        ] == k
+        k = sess.query(Keyword).filter(with_parent(i1, Item.keywords)).all()
+        eq_(
+            k,
+            [
+                Keyword(name="red"),
+                Keyword(name="small"),
+                Keyword(name="square"),
+            ],
+        )
 
     def test_with_transient(self):
         User, Order = self.classes.User, self.classes.Order
@@ -6565,16 +6511,6 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
         q = sess.query(User)
         u1 = q.filter_by(name="jack").one()
         utrans = User(id=u1.id)
-        o = sess.query(Order).with_parent(utrans, User.orders)
-        eq_(
-            [
-                Order(description="order 1"),
-                Order(description="order 3"),
-                Order(description="order 5"),
-            ],
-            o.all(),
-        )
-
         o = sess.query(Order).filter(with_parent(utrans, User.orders))
         eq_(
             [
@@ -6593,10 +6529,6 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
         o1 = sess.query(Order).first()
         opending = Order(id=20, user_id=o1.user_id)
         sess.add(opending)
-        eq_(
-            sess.query(User).with_parent(opending, Order.user).one(),
-            User(id=o1.user_id),
-        )
         eq_(
             sess.query(User).filter(with_parent(opending, Order.user)).one(),
             User(id=o1.user_id),
@@ -6611,7 +6543,7 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
         opending = Order(user_id=o1.user_id)
         sess.add(opending)
         eq_(
-            sess.query(User).with_parent(opending, Order.user).one(),
+            sess.query(User).filter(with_parent(opending, Order.user)).one(),
             User(id=o1.user_id),
         )
 
@@ -6622,8 +6554,8 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
         sess = fixture_session()
         u1, u2 = sess.query(User).order_by(User.id)[0:2]
 
-        q1 = sess.query(Address).with_parent(u1, User.addresses)
-        q2 = sess.query(Address).with_parent(u2, User.addresses)
+        q1 = sess.query(Address).filter(with_parent(u1, User.addresses))
+        q2 = sess.query(Address).filter(with_parent(u2, User.addresses))
 
         self.assert_compile(
             q1.union(q2),
@@ -6870,7 +6802,9 @@ class WithTransientOnNone(_fixtures.FixtureTest, AssertsCompiledSQL):
 
         sess = fixture_session()
 
-        q = sess.query(User).with_parent(Address(user_id=None), Address.user)
+        q = sess.query(User).filter(
+            with_parent(Address(user_id=None), Address.user)
+        )
         with expect_warnings("Got None for value of column"):
             self.assert_compile(
                 q,
@@ -6884,8 +6818,10 @@ class WithTransientOnNone(_fixtures.FixtureTest, AssertsCompiledSQL):
         User, Address = self.classes.User, self.classes.Address
 
         s = fixture_session()
-        q = s.query(User).with_parent(
-            Address(user_id=None, email_address=None), Address.special_user
+        q = s.query(User).filter(
+            with_parent(
+                Address(user_id=None, email_address=None), Address.special_user
+            )
         )
         with expect_warnings("Got None for value of column"):
 
@@ -7105,7 +7041,7 @@ class SynonymTest(QueryTest, AssertsCompiledSQL):
 
                 u1 = q.filter_by(**{nameprop: "jack"}).one()
 
-                o = sess.query(Order).with_parent(u1, property=orderprop).all()
+                o = sess.query(Order).filter(with_parent(u1, orderprop)).all()
                 assert [
                     Order(description="order 1"),
                     Order(description="order 3"),
index 2547a981a22ac648cf977ac7202f710e73cf7570..2d3a6309b02f42b9da77816a36e2d199f169b512 100644 (file)
@@ -628,8 +628,10 @@ class DirectSelfRefFKTest(fixtures.MappedTest, AssertsCompiledSQL):
         self._descendants_fixture(data=False)
         Entity = self.classes.Entity
         sess = fixture_session()
+
+        da = aliased(Entity)
         self.assert_compile(
-            sess.query(Entity).join(Entity.descendants, aliased=True),
+            sess.query(Entity).join(Entity.descendants.of_type(da)),
             "SELECT entity.path AS entity_path FROM entity JOIN entity AS "
             "entity_1 ON entity_1.path LIKE entity.path || :path_1",
         )
@@ -1451,13 +1453,15 @@ class CompositeSelfRefFKTest(fixtures.MappedTest, AssertsCompiledSQL):
 
     def _test_join_aliasing(self, sess):
         Employee = self.classes.Employee
+        ea = aliased(Employee)
         eq_(
             [
                 n
                 for n, in sess.query(Employee.name)
-                .join(Employee.reports_to, aliased=True)
-                .filter_by(name="emp5")
-                .reset_joinpoint()
+                .join(Employee.reports_to.of_type(ea))
+                .filter(ea.name == "emp5")
+                # broken until #7244 is fixed due to of_type() usage
+                # .filter_by(name="emp5")
                 .order_by(Employee.name)
             ],
             ["emp6", "emp7"],