summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2021-10-26 15:37:16 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2021-10-27 11:41:00 -0400
commit0f98ead274df103a5a623baa3898580932e1c8e2 (patch)
treef2c9acf56b9144e7ce3817a6066fedbe481ef540
parentd2cf7dcfe0cd7e9986376b6e7edd4b7d60108599 (diff)
downloadsqlalchemy-0f98ead274df103a5a623baa3898580932e1c8e2.tar.gz
deprecation warnings: with_parent, aliased, from_joinpoint
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
-rw-r--r--lib/sqlalchemy/exc.py15
-rw-r--r--lib/sqlalchemy/orm/query.py1
-rw-r--r--lib/sqlalchemy/testing/assertions.py2
-rw-r--r--lib/sqlalchemy/testing/warnings.py3
-rw-r--r--lib/sqlalchemy/util/deprecations.py9
-rw-r--r--test/base/test_except.py2
-rw-r--r--test/orm/inheritance/test_deprecations.py260
-rw-r--r--test/orm/inheritance/test_polymorphic_rel.py82
-rw-r--r--test/orm/inheritance/test_single.py32
-rw-r--r--test/orm/test_cascade.py3
-rw-r--r--test/orm/test_deprecations.py289
-rw-r--r--test/orm/test_lambdas.py20
-rw-r--r--test/orm/test_lazy_relations.py5
-rw-r--r--test/orm/test_naturalpks.py5
-rw-r--r--test/orm/test_query.py148
-rw-r--r--test/orm/test_relationships.py12
16 files changed, 631 insertions, 257 deletions
diff --git a/lib/sqlalchemy/exc.py b/lib/sqlalchemy/exc.py
index 9b9c82aca..7fa77120c 100644
--- a/lib/sqlalchemy/exc.py
+++ b/lib/sqlalchemy/exc.py
@@ -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."""
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index d48e34923..eddb9c29c 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -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:
diff --git a/lib/sqlalchemy/testing/assertions.py b/lib/sqlalchemy/testing/assertions.py
index 986dbb5e9..6bf14aecd 100644
--- a/lib/sqlalchemy/testing/assertions.py
+++ b/lib/sqlalchemy/testing/assertions.py
@@ -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):
diff --git a/lib/sqlalchemy/testing/warnings.py b/lib/sqlalchemy/testing/warnings.py
index 40d03f0af..f29d65692 100644
--- a/lib/sqlalchemy/testing/warnings.py
+++ b/lib/sqlalchemy/testing/warnings.py
@@ -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",
#
diff --git a/lib/sqlalchemy/util/deprecations.py b/lib/sqlalchemy/util/deprecations.py
index c1acde39b..e1138aaef 100644
--- a/lib/sqlalchemy/util/deprecations.py
+++ b/lib/sqlalchemy/util/deprecations.py
@@ -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,
)
diff --git a/test/base/test_except.py b/test/base/test_except.py
index be6f448bd..767fd233c 100644
--- a/test/base/test_except.py
+++ b/test/base/test_except.py
@@ -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
index 000000000..bf8219cb0
--- /dev/null
+++ b/test/orm/inheritance/test_deprecations.py
@@ -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])",
+ )
diff --git a/test/orm/inheritance/test_polymorphic_rel.py b/test/orm/inheritance/test_polymorphic_rel.py
index b9d9ad932..2e52e162b 100644
--- a/test/orm/inheritance/test_polymorphic_rel.py
+++ b/test/orm/inheritance/test_polymorphic_rel.py
@@ -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
diff --git a/test/orm/inheritance/test_single.py b/test/orm/inheritance/test_single.py
index 066f781d8..025f717d7 100644
--- a/test/orm/inheritance/test_single.py
+++ b/test/orm/inheritance/test_single.py
@@ -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 = (
diff --git a/test/orm/test_cascade.py b/test/orm/test_cascade.py
index 3e42368df..8749a0147 100644
--- a/test/orm/test_cascade.py
+++ b/test/orm/test_cascade.py
@@ -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")],
)
diff --git a/test/orm/test_deprecations.py b/test/orm/test_deprecations.py
index c66259e47..f6854cf27 100644
--- a/test/orm/test_deprecations.py
+++ b/test/orm/test_deprecations.py
@@ -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},
+ )
diff --git a/test/orm/test_lambdas.py b/test/orm/test_lambdas.py
index 30048585b..5274271d9 100644
--- a/test/orm/test_lambdas.py
+++ b/test/orm/test_lambdas.py
@@ -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(
diff --git a/test/orm/test_lazy_relations.py b/test/orm/test_lazy_relations.py
index 2bea9b110..412637ec6 100644
--- a/test/orm/test_lazy_relations.py
+++ b/test/orm/test_lazy_relations.py
@@ -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(),
)
diff --git a/test/orm/test_naturalpks.py b/test/orm/test_naturalpks.py
index 9f31c4b80..fba335d28 100644
--- a/test/orm/test_naturalpks.py
+++ b/test/orm/test_naturalpks.py
@@ -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):
diff --git a/test/orm/test_query.py b/test/orm/test_query.py
index 1971964b1..a504a4eb1 100644
--- a/test/orm/test_query.py
+++ b/test/orm/test_query.py
@@ -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_(
[
@@ -6594,10 +6530,6 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
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"),
diff --git a/test/orm/test_relationships.py b/test/orm/test_relationships.py
index 2547a981a..2d3a6309b 100644
--- a/test/orm/test_relationships.py
+++ b/test/orm/test_relationships.py
@@ -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"],