]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
deprecation warnings: strings in loader options, join, with_parent
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 20 Oct 2021 16:50:53 +0000 (12:50 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 25 Oct 2021 16:09:41 +0000 (12:09 -0400)
Repairs one in-library deprecation warning regarding
mapper propagation of options

raises maxfail to 250, as 25 is too low when we are trying
to address many errors at once.   the 25 was originally
due to the fact that our fixtures would be broken after
that many failures in most cases, which today should not
be the case nearly as often.

Change-Id: I26affddf42e2cae2aaf9561633e9b8cd431eb189

35 files changed:
lib/sqlalchemy/ext/associationproxy.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/strategy_options.py
lib/sqlalchemy/testing/warnings.py
setup.cfg
test/ext/test_associationproxy.py
test/ext/test_horizontal_shard.py
test/orm/inheritance/test_assorted_poly.py
test/orm/inheritance/test_basic.py
test/orm/inheritance/test_polymorphic_rel.py
test/orm/inheritance/test_relationship.py
test/orm/inheritance/test_single.py
test/orm/test_ac_relationships.py
test/orm/test_assorted_eager.py
test/orm/test_cache_key.py
test/orm/test_composites.py
test/orm/test_default_strategies.py
test/orm/test_deferred.py
test/orm/test_deprecations.py
test/orm/test_eager_relations.py
test/orm/test_expire.py
test/orm/test_froms.py
test/orm/test_generative.py
test/orm/test_joins.py
test/orm/test_lazy_relations.py
test/orm/test_mapper.py
test/orm/test_merge.py
test/orm/test_of_type.py
test/orm/test_options.py
test/orm/test_pickled.py
test/orm/test_query.py
test/orm/test_relationships.py
test/orm/test_selectin_relations.py
test/orm/test_subquery_relations.py
test/orm/test_unitofwork.py

index 411033a6bfe6607b59b27fe6bc8e6cecf53dda5b..dd5c10ac956039a98d87c338e39d3ec2864a1df5 100644 (file)
@@ -472,10 +472,23 @@ class AssociationProxyInstance(object):
     def attr(self):
         """Return a tuple of ``(local_attr, remote_attr)``.
 
-        This attribute is convenient when specifying a join
-        using :meth:`_query.Query.join` across two relationships::
+        This attribute was originally intended to facilitate using the
+        :meth:`_query.Query.join` method to join across the two relationships
+        at once, however this makes use of a deprecated calling style.
+
+        To use :meth:`_sql.select.join` or :meth:`_orm.Query.join` with
+        an association proxy, the current method is to make use of the
+        :attr:`.AssociationProxyInstance.local_attr` and
+        :attr:`.AssociationProxyInstance.remote_attr` attributes separately::
+
+            stmt = (
+                select(Parent).
+                join(Parent.proxied.local_attr).
+                join(Parent.proxied.remote_attr)
+            )
 
-            sess.query(Parent).join(*Parent.proxied.attr)
+        A future release may seek to provide a more succinct join pattern
+        for association proxy attributes.
 
         .. seealso::
 
index 1aa6666a55c7ab581a9cb73cb003d3d7e026d580..4de12b88c772121b7b578b3c687a7053bb001e82 100644 (file)
@@ -3111,14 +3111,20 @@ class Mapper(
                 # "enable" options, to turn on the properties that we want to
                 # load by default (subject to options from the query)
                 enable_opt.set_generic_strategy(
-                    (prop.key,), dict(prop.strategy_key)
+                    # convert string name to an attribute before passing
+                    # to loader strategy
+                    (getattr(entity.entity_namespace, prop.key),),
+                    dict(prop.strategy_key),
                 )
             else:
                 # "disable" options, to turn off the properties from the
                 # superclass that we *don't* want to load, applied after
                 # the options from the query to override them
                 disable_opt.set_generic_strategy(
-                    (prop.key,), {"do_nothing": True}
+                    # convert string name to an attribute before passing
+                    # to loader strategy
+                    (getattr(entity.entity_namespace, prop.key),),
+                    {"do_nothing": True},
                 )
 
         primary_key = [
index b7ed4e89bbf56d5b932a935ede5ce32c2086034b..675c7218bd64cdc21380cc5876b78a63fa30b9af 100644 (file)
@@ -301,6 +301,7 @@ class Load(Generative, LoaderOption):
                 )
 
         if isinstance(attr, util.string_types):
+
             default_token = attr.endswith(_DEFAULT_TOKEN)
             attr_str_name = attr
             if attr.endswith(_WILDCARD_TOKEN) or default_token:
@@ -328,7 +329,7 @@ class Load(Generative, LoaderOption):
                 "Using strings to indicate column or "
                 "relationship paths in loader options is deprecated "
                 "and will be removed in SQLAlchemy 2.0.  Please use "
-                "the class-bound attribute directly."
+                "the class-bound attribute directly.",
             )
             try:
                 # use getattr on the class to work around
index 9e02a0c03bdbd690a9be66a3d37bd346342ac63c..9886b122d104c9b25ca947c78e46aef5a174b627 100644 (file)
@@ -73,16 +73,8 @@ def setup_filters():
         r"The Query\.with_parent\(\) method",
         r"The Query\.select_entity_from\(\) method",
         r"The ``aliased`` and ``from_joinpoint`` keyword arguments",
-        r"Using strings to indicate relationship names in Query.join",
-        r"Using strings to indicate column or relationship paths in "
-        "loader options",
-        r"Using strings to indicate relationship names in the ORM "
-        r"with_parent\(\)",
         r"The Query.with_polymorphic\(\) method is considered "
         "legacy as of the 1.x series",
-        r"Passing a chain of multiple join conditions to Query.join\(\) "
-        r"is deprecated and will be removed in SQLAlchemy 2.0.",
-        r"Query.join\(\) will no longer accept tuples as arguments",
         #
         # ORM Session
         #
index 7df0ad5c850f35b3209bf91e307832dc02e5fc66..f432561b189ea398d3e0c407c53f1a093911fdc6 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -92,7 +92,7 @@ tag_build = dev
 where = lib
 
 [tool:pytest]
-addopts = --tb native -v -r sfxX --maxfail=25 -p no:warnings -p no:logging
+addopts = --tb native -v -r sfxX --maxfail=250 -p no:warnings -p no:logging
 python_files = test/*test_*.py
 
 [upload]
index bbb430a6f537876576b3cd570234b6f5702cea9f..258ecb90c4f6988af7c0d81ef70176de8a3ef76b 100644 (file)
@@ -2277,9 +2277,9 @@ class ComparatorTest(fixtures.MappedTest, AssertsCompiledSQL):
     def test_join_separate_attr(self):
         User = self.classes.User
         self.assert_compile(
-            self.session.query(User).join(
-                User.keywords.local_attr, User.keywords.remote_attr
-            ),
+            self.session.query(User)
+            .join(User.keywords.local_attr)
+            .join(User.keywords.remote_attr),
             "SELECT users.id AS users_id, users.name AS users_name, "
             "users.singular_id AS users_singular_id "
             "FROM users JOIN userkeywords ON users.id = "
@@ -2290,7 +2290,9 @@ class ComparatorTest(fixtures.MappedTest, AssertsCompiledSQL):
     def test_join_single_attr(self):
         User = self.classes.User
         self.assert_compile(
-            self.session.query(User).join(*User.keywords.attr),
+            self.session.query(User)
+            .join(User.keywords.attr[0])
+            .join(User.keywords.attr[1]),
             "SELECT users.id AS users_id, users.name AS users_name, "
             "users.singular_id AS users_singular_id "
             "FROM users JOIN userkeywords ON users.id = "
index a0bc8bbe5498ce30fa53d9b955ced3311fcfa354..192617901e468e70e657309d2115813562839e6f 100644 (file)
@@ -863,7 +863,7 @@ class SelectinloadRegressionTest(fixtures.DeclarativeMappedTest):
         session.add(book)
         session.commit()
 
-        result = session.query(Book).options(selectinload("pages")).all()
+        result = session.query(Book).options(selectinload(Book.pages)).all()
         eq_(result, [book])
 
 
index 2db4641ddd66138310c6b37b27c2352f8ebceccf..729e1ee0479578847b1cb62a562776b7e5ff9b4d 100644 (file)
@@ -641,7 +641,7 @@ class RelationshipTest4(fixtures.MappedTest):
 
         def go():
             testcar = session.get(
-                Car, car1.car_id, options=[joinedload("employee")]
+                Car, car1.car_id, options=[joinedload(Car.employee)]
             )
             assert str(testcar.employee) == "Engineer E4, status X"
 
@@ -662,7 +662,7 @@ class RelationshipTest4(fixtures.MappedTest):
             testcar = session.get(
                 Car,
                 car1.car_id,
-                options=[joinedload("employee")],
+                options=[joinedload(Car.employee)],
             )
             assert str(testcar.employee) == "Engineer E4, status X"
 
@@ -670,7 +670,7 @@ class RelationshipTest4(fixtures.MappedTest):
 
         session.expunge_all()
         s = session.query(Car)
-        c = s.join("employee").filter(Person.name == "E4")[0]
+        c = s.join(Car.employee).filter(Person.name == "E4")[0]
         assert c.car_id == car1.car_id
 
 
@@ -1449,7 +1449,7 @@ class GenerativeTest(fixtures.MappedTest, AssertsExecutionResults):
         r = (
             session.query(Person)
             .filter(Person.name.like("%2"))
-            .join("status")
+            .join(Person.status)
             .filter_by(name="active")
             .order_by(Person.person_id)
         )
@@ -1472,7 +1472,7 @@ class GenerativeTest(fixtures.MappedTest, AssertsExecutionResults):
         session = fixture_session()
         r = (
             session.query(Engineer)
-            .join("status")
+            .join(Engineer.status)
             .filter(
                 Person.name.in_(["E2", "E3", "E4", "M4", "M2", "M1"])
                 & (Status.name == "active")
@@ -2226,7 +2226,7 @@ class Ticket2419Test(fixtures.DeclarativeMappedTest):
 
         s.commit()
 
-        q = s.query(B, B.ds.any(D.id == 1)).options(joinedload("es"))
+        q = s.query(B, B.ds.any(D.id == 1)).options(joinedload(B.es))
         q = q.join(C, C.b_id == B.id)
         q = q.limit(5)
         eq_(q.all(), [(b, True)])
index 52b362b9efee72c50a3ead93b0fd3da25902e902..e870a80f0d85f9e889d2069b78b4ca2d411a63cd 100644 (file)
@@ -224,7 +224,7 @@ class PolyExpressionEagerLoad(fixtures.DeclarativeMappedTest):
         result = (
             session.query(A)
             .filter_by(child_id=None)
-            .options(joinedload("child"))
+            .options(joinedload(A.child))
             .one()
         )
 
index 1a96ee011e503b7097350c0ccfb7bd8b93176c14..b9d9ad932b438addf1dcecdb9a46a8781f7e68db 100644 (file)
@@ -332,7 +332,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         sess = fixture_session()
         eq_(
             sess.query(Person)
-            .join("paperwork")
+            .join(Person.paperwork)
             .filter(Paperwork.description.like("%review%"))
             .all(),
             [b1, m1],
@@ -357,7 +357,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         eq_(
             sess.query(Person)
             .order_by(Person.person_id)
-            .join("paperwork")
+            .join(Person.paperwork)
             .filter(Paperwork.description.like("%#2%"))
             .all(),
             [e1, m1],
@@ -368,7 +368,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         eq_(
             sess.query(Engineer)
             .order_by(Person.person_id)
-            .join("paperwork")
+            .join(Person.paperwork)
             .filter(Paperwork.description.like("%#2%"))
             .all(),
             [e1],
@@ -379,7 +379,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         eq_(
             sess.query(Person)
             .order_by(Person.person_id)
-            .join("paperwork")
+            .join(Person.paperwork)
             .filter(Person.name.like("%dog%"))
             .filter(Paperwork.description.like("%#2%"))
             .all(),
@@ -391,7 +391,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         eq_(
             sess.query(Person)
             .order_by(Person.person_id)
-            .join("paperwork", aliased=True)
+            .join(Person.paperwork, aliased=True)
             .filter(Paperwork.description.like("%review%"))
             .all(),
             [b1, m1],
@@ -420,7 +420,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         eq_(
             sess.query(Person)
             .order_by(Person.person_id)
-            .join(pa, "paperwork")
+            .join(pa, Person.paperwork)
             .filter(pa.description.like("%review%"))
             .all(),
             [b1, m1],
@@ -431,7 +431,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         eq_(
             sess.query(Person)
             .order_by(Person.person_id)
-            .join("paperwork", aliased=True)
+            .join(Person.paperwork, aliased=True)
             .filter(Paperwork.description.like("%#2%"))
             .all(),
             [e1, m1],
@@ -443,7 +443,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         eq_(
             sess.query(Person)
             .order_by(Person.person_id)
-            .join(pa, "paperwork")
+            .join(pa, Person.paperwork)
             .filter(pa.description.like("%#2%"))
             .all(),
             [e1, m1],
@@ -454,7 +454,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         eq_(
             sess.query(Engineer)
             .order_by(Person.person_id)
-            .join("paperwork", aliased=True)
+            .join(Person.paperwork, aliased=True)
             .filter(Paperwork.description.like("%#2%"))
             .all(),
             [e1],
@@ -466,7 +466,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         eq_(
             sess.query(Engineer)
             .order_by(Person.person_id)
-            .join(pa, "paperwork")
+            .join(pa, Person.paperwork)
             .filter(pa.description.like("%#2%"))
             .all(),
             [e1],
@@ -478,7 +478,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         eq_(
             sess.query(Person)
             .order_by(Person.person_id)
-            .join(pa, "paperwork")
+            .join(pa, Person.paperwork)
             .filter(Person.name.like("%dog%"))
             .filter(pa.description.like("%#2%"))
             .all(),
@@ -491,7 +491,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
             sess.query(Person)
             .with_polymorphic(Manager)
             .order_by(Person.person_id)
-            .join("paperwork")
+            .join(Person.paperwork)
             .filter(Paperwork.description.like("%review%"))
             .all(),
             [b1, m1],
@@ -520,7 +520,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
             sess.query(Person)
             .with_polymorphic([Manager, Engineer])
             .order_by(Person.person_id)
-            .join("paperwork")
+            .join(Person.paperwork)
             .filter(Paperwork.description.like("%#2%"))
             .all(),
             [e1, m1],
@@ -532,7 +532,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
             sess.query(Person)
             .with_polymorphic([Manager, Engineer])
             .order_by(Person.person_id)
-            .join("paperwork")
+            .join(Person.paperwork)
             .filter(Person.name.like("%dog%"))
             .filter(Paperwork.description.like("%#2%"))
             .all(),
@@ -544,7 +544,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         eq_(
             sess.query(Person)
             .with_polymorphic(Manager)
-            .join("paperwork", aliased=True)
+            .join(Person.paperwork, aliased=True)
             .filter(Paperwork.description.like("%review%"))
             .all(),
             [b1, m1],
@@ -556,7 +556,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         eq_(
             sess.query(Person)
             .with_polymorphic(Manager)
-            .join(pa, "paperwork")
+            .join(pa, Person.paperwork)
             .filter(pa.description.like("%review%"))
             .all(),
             [b1, m1],
@@ -568,7 +568,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
             sess.query(Person)
             .with_polymorphic([Manager, Engineer])
             .order_by(Person.person_id)
-            .join("paperwork", aliased=True)
+            .join(Person.paperwork, aliased=True)
             .filter(Paperwork.description.like("%#2%"))
             .all(),
             [e1, m1],
@@ -581,7 +581,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
             sess.query(Person)
             .with_polymorphic([Manager, Engineer])
             .order_by(Person.person_id)
-            .join(pa, "paperwork")
+            .join(pa, Person.paperwork)
             .filter(pa.description.like("%#2%"))
             .all(),
             [e1, m1],
@@ -595,7 +595,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
             sess.query(Person)
             .with_polymorphic([Manager, Engineer])
             .order_by(Person.person_id)
-            .join(pa, "paperwork")
+            .join(pa, Person.paperwork)
             .filter(Person.name.like("%dog%"))
             .filter(pa.description.like("%#2%"))
             .all(),
@@ -606,7 +606,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         sess = fixture_session()
         eq_(
             sess.query(Company)
-            .join("employees")
+            .join(Company.employees)
             .filter(Person.name == "vlad")
             .one(),
             c2,
@@ -616,7 +616,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         sess = fixture_session()
         eq_(
             sess.query(Company)
-            .join("employees", aliased=True)
+            .join(Company.employees, aliased=True)
             .filter(Person.name == "vlad")
             .one(),
             c2,
@@ -627,7 +627,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         ea = aliased(Person)
         eq_(
             sess.query(Company)
-            .join(ea, "employees")
+            .join(ea, Company.employees)
             .filter(ea.name == "vlad")
             .one(),
             c2,
@@ -646,7 +646,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         any_ = Company.employees.any(Person.name == "wally")
         eq_(
             sess.query(Company)
-            .join("employees", aliased=True)
+            .join(Company.employees, aliased=True)
             .filter(Person.name == "dilbert")
             .filter(any_)
             .all(),
@@ -1172,7 +1172,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         ealias = aliased(Engineer)
         eq_(
             sess.query(Company)
-            .join(ealias, "employees")
+            .join(ealias, Company.employees)
             .filter(ealias.primary_language == "java")
             .all(),
             [c1],
@@ -1231,7 +1231,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         sess = fixture_session()
         eq_(
             sess.query(Company)
-            .join("employees")
+            .join(Company.employees)
             .filter(Engineer.primary_language == "java")
             .all(),
             [c1],
@@ -1264,7 +1264,10 @@ class _PolymorphicTestBase(fixtures.NoCache):
     def test_join_to_subclass_fourteen(self):
         sess = fixture_session()
         eq_(
-            sess.query(Company).join("employees", Engineer.machines).all(),
+            sess.query(Company)
+            .join(Company.employees)
+            .join(Engineer.machines)
+            .all(),
             [c1, c2],
         )
 
@@ -1272,7 +1275,8 @@ class _PolymorphicTestBase(fixtures.NoCache):
         sess = fixture_session()
         eq_(
             sess.query(Company)
-            .join("employees", Engineer.machines)
+            .join(Company.employees)
+            .join(Engineer.machines)
             .filter(Machine.name.ilike("%thinkpad%"))
             .all(),
             [c1],
@@ -1365,7 +1369,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         sess = fixture_session()
         eq_(
             sess.query(Company)
-            .join("employees")
+            .join(Company.employees)
             .filter(Person.name.in_(["dilbert", "vlad"]))
             .join(Person.paperwork)
             .filter(Paperwork.description.like("%#2%"))
@@ -1377,7 +1381,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         sess = fixture_session()
         eq_(
             sess.query(Company)
-            .join("employees")
+            .join(Company.employees)
             .filter(Person.name.in_(["dilbert", "vlad"]))
             .join(Person.paperwork)
             .filter(Paperwork.description.like("%#%"))
@@ -1445,7 +1449,7 @@ class _PolymorphicTestBase(fixtures.NoCache):
         pa = aliased(Paperwork)
         eq_(
             sess.query(Company)
-            .join(ea, "employees")
+            .join(ea, Company.employees)
             .filter(ea.name.in_(["dilbert", "vlad"]))
             .join(pa, ea.paperwork)
             .filter(pa.description.like("%#2%"))
index e795f0c7ff7692d8e394a86c32852a6024045f9b..881353e3bb7d4b169bbff8ef95f81629b36aec06 100644 (file)
@@ -164,7 +164,7 @@ class SelfReferentialTestJoinedToBase(fixtures.MappedTest):
         pa = aliased(Person)
         eq_(
             sess.query(Engineer)
-            .join(pa, "reports_to")
+            .join(pa, Engineer.reports_to)
             .filter(pa.name == "dogbert")
             .first(),
             Engineer(name="dilbert"),
@@ -276,7 +276,7 @@ class SelfReferentialJ2JTest(fixtures.MappedTest):
 
         eq_(
             sess.query(Engineer)
-            .join(ma, "reports_to")
+            .join(ma, Engineer.reports_to)
             .filter(ma.name == "dogbert")
             .first(),
             Engineer(name="dilbert"),
@@ -507,7 +507,7 @@ class SelfReferentialJ2JSelfTest(fixtures.MappedTest):
         ea = aliased(Engineer)
         eq_(
             sess.query(Engineer)
-            .join(ea, "reports_to")
+            .join(ea, Engineer.reports_to)
             .filter(ea.name == "wally")
             .first(),
             Engineer(name="dilbert"),
@@ -937,7 +937,7 @@ class SelfReferentialM2MTest(fixtures.MappedTest, AssertsCompiledSQL):
 
         # test that the splicing of the join works here, doesn't break in
         # the middle of "parent join child1"
-        q = sess.query(Child1).options(joinedload("left_child2"))
+        q = sess.query(Child1).options(joinedload(Child1.left_child2))
         self.assert_compile(
             q.limit(1).statement,
             "SELECT child1.id, parent.id AS id_1, parent.cls, "
@@ -969,7 +969,7 @@ class SelfReferentialM2MTest(fixtures.MappedTest, AssertsCompiledSQL):
         sess.flush()
         sess.expunge_all()
 
-        query_ = sess.query(Child1).options(subqueryload("left_child2"))
+        query_ = sess.query(Child1).options(subqueryload(Child1.left_child2))
         for row in query_.all():
             assert row.left_child2
 
@@ -1283,7 +1283,9 @@ class SubClassEagerToSubClassTest(fixtures.MappedTest):
 
         def go():
             eq_(
-                sess.query(Subparent).options(joinedload("children")).all(),
+                sess.query(Subparent)
+                .options(joinedload(Subparent.children))
+                .all(),
                 [p1, p2],
             )
 
@@ -1311,7 +1313,7 @@ class SubClassEagerToSubClassTest(fixtures.MappedTest):
             eq_(
                 sess.query(Subparent)
                 .join(Subparent.children)
-                .options(contains_eager("children"))
+                .options(contains_eager(Subparent.children))
                 .all(),
                 [p1, p2],
             )
@@ -1337,7 +1339,9 @@ class SubClassEagerToSubClassTest(fixtures.MappedTest):
 
         def go():
             eq_(
-                sess.query(Subparent).options(subqueryload("children")).all(),
+                sess.query(Subparent)
+                .options(subqueryload(Subparent.children))
+                .all(),
                 [p1, p2],
             )
 
@@ -1770,7 +1774,8 @@ class SubClassToSubClassMultiTest(AssertsCompiledSQL, fixtures.MappedTest):
         s = fixture_session()
         self.assert_compile(
             s.query(Parent)
-            .join(Parent.sub1, Sub1.sub2)
+            .join(Parent.sub1)
+            .join(Sub1.sub2)
             .join(Sub2.ep1)
             .join(Sub2.ep2),
             "SELECT parent.id AS parent_id, parent.data AS parent_data "
index a3230f91d80b7d3b35bc2f436add2b15e53bc2a0..690bdfc577a37deeb707441bc9c94d8ad675eb23 100644 (file)
@@ -764,7 +764,7 @@ class RelationshipFromSingleTest(
         sess = fixture_session()
 
         with self.sql_execution_asserter(testing.db) as asserter:
-            sess.query(Manager).options(subqueryload("stuff")).all()
+            sess.query(Manager).options(subqueryload(Manager.stuff)).all()
 
         asserter.assert_(
             CompiledSQL(
@@ -992,7 +992,7 @@ class RelationshipToSingleTest(
 
         sess = fixture_session()
         self.assert_compile(
-            sess.query(Company, Engineer.name).outerjoin("engineers"),
+            sess.query(Company, Engineer.name).outerjoin(Company.engineers),
             "SELECT companies.company_id AS companies_company_id, "
             "companies.name AS companies_name, "
             "employees.name AS employees_name "
@@ -1378,7 +1378,7 @@ class RelationshipToSingleTest(
         sess.expunge_all()
         eq_(
             sess.query(Company)
-            .options(joinedload("engineers"))
+            .options(joinedload(Company.engineers))
             .order_by(Company.name)
             .all(),
             [
index 26dd674800f9ff281089741ce80b11ae48cb7799..6a050b698c68511e7f208e29fa2de40ad156890d 100644 (file)
@@ -49,7 +49,7 @@ class PartitionByFixture(fixtures.DeclarativeMappedTest):
             .label("index"),
         ).alias()
 
-        partitioned_b = cls.partitioned_b = aliased(B, alias=partition)
+        cls.partitioned_b = partitioned_b = aliased(B, alias=partition)
 
         A.partitioned_bs = relationship(
             partitioned_b,
@@ -141,7 +141,7 @@ class AliasedClassRelationshipTest(
 
         self.assert_sql_count(testing.db, go, 2)
 
-    @testing.combinations("string", "ac_attribute", "ac_attr_w_of_type")
+    @testing.combinations("ac_attribute", "ac_attr_w_of_type")
     def test_selectinload_w_joinedload_after(self, calling_style):
         """test has been enhanced to also test #7224"""
 
@@ -151,9 +151,7 @@ class AliasedClassRelationshipTest(
 
         partitioned_b = self.partitioned_b
 
-        if calling_style == "string":
-            opt = selectinload(A.partitioned_bs).joinedload("cs")
-        elif calling_style == "ac_attribute":
+        if calling_style == "ac_attribute":
             opt = selectinload(A.partitioned_bs).joinedload(partitioned_b.cs)
         elif calling_style == "ac_attr_w_of_type":
             # this would have been a workaround for people who encountered
index 6f983e0ed13b51a568970968fd9ddb7772cb7b7c..a97538da64c2f957e1ae8175e860d8a7e67b7723 100644 (file)
@@ -259,7 +259,7 @@ class EagerTest(fixtures.MappedTest):
         )
 
         s = fixture_session()
-        q = s.query(Thing).options(sa.orm.joinedload("category"))
+        q = s.query(Thing).options(sa.orm.joinedload(Thing.category))
 
         result = q.select_from(
             tests.outerjoin(
@@ -292,7 +292,7 @@ class EagerTest(fixtures.MappedTest):
         )
 
         s = fixture_session()
-        q = s.query(Thing).options(sa.orm.joinedload("category"))
+        q = s.query(Thing).options(sa.orm.joinedload(Thing.category))
         result = q.filter(
             sa.and_(
                 tests.c.owner_id == 1,
@@ -301,7 +301,7 @@ class EagerTest(fixtures.MappedTest):
                     options.c.someoption == False,  # noqa
                 ),
             )
-        ).outerjoin("owner_option")
+        ).outerjoin(Thing.owner_option)
 
         result_str = ["%d %s" % (t.id, t.category.name) for t in result]
         eq_(result_str, ["1 Some Category", "3 Some Category"])
@@ -311,13 +311,13 @@ class EagerTest(fixtures.MappedTest):
         Thing, tests = (self.classes.Thing, self.tables.tests)
 
         s = fixture_session()
-        q = s.query(Thing).options(sa.orm.joinedload("category"))
+        q = s.query(Thing).options(sa.orm.joinedload(Thing.category))
         result = q.filter(
             (tests.c.owner_id == 1)
             & text(
                 "options.someoption is null or options.someoption=:opt"
             ).bindparams(opt=False)
-        ).join("owner_option")
+        ).join(Thing.owner_option)
 
         result_str = ["%d %s" % (t.id, t.category.name) for t in result]
         eq_(result_str, ["3 Some Category"])
@@ -330,14 +330,14 @@ class EagerTest(fixtures.MappedTest):
         )
 
         s = fixture_session()
-        q = s.query(Thing).options(sa.orm.joinedload("category"))
+        q = s.query(Thing).options(sa.orm.joinedload(Thing.category))
         result = q.filter(
             (tests.c.owner_id == 1)
             & (
                 (options.c.someoption == None)
                 | (options.c.someoption == False)
             )  # noqa
-        ).join("owner_option")
+        ).join(Thing.owner_option)
 
         result_str = ["%d %s" % (t.id, t.category.name) for t in result]
         eq_(result_str, ["3 Some Category"])
@@ -552,7 +552,7 @@ class EagerTest3(fixtures.MappedTest):
         # "order by max desc" separately
         q = (
             session.query(Data)
-            .options(sa.orm.joinedload("foo"))
+            .options(sa.orm.joinedload(Data.foo))
             .select_from(
                 datas.join(arb_data, arb_data.c.data_id == datas.c.id)
             )
@@ -639,7 +639,7 @@ class EagerTest4(fixtures.MappedTest):
 
         q = (
             sess.query(Department)
-            .join("employees")
+            .join(Department.employees)
             .filter(Employee.name.startswith("J"))
             .distinct()
             .order_by(sa.desc(Department.name))
@@ -1317,10 +1317,10 @@ class EagerTest9(fixtures.MappedTest):
             acc = (
                 session.query(Account)
                 .options(
-                    sa.orm.joinedload("entries")
-                    .joinedload("transaction")
-                    .joinedload("entries")
-                    .joinedload("account")
+                    sa.orm.joinedload(Account.entries)
+                    .joinedload(Entry.transaction)
+                    .joinedload(Transaction.entries)
+                    .joinedload(Entry.account)
                 )
                 .order_by(Account.account_id)
             ).first()
index f25a57fe5a9b515b11e5b5b6d8af1307968104f1..d689bd6bad126acf95543680c02c8bcc9f9ca21d 100644 (file)
@@ -249,6 +249,8 @@ class CacheKeyTest(CacheKeyFixture, _fixtures.FixtureTest):
             "User", "Address", "Keyword", "Order", "Item"
         )
 
+        a1 = aliased(Address)
+
         self._run_cache_key_fixture(
             lambda: (
                 Load(User).joinedload(User.addresses),
@@ -261,10 +263,10 @@ class CacheKeyTest(CacheKeyFixture, _fixtures.FixtureTest):
                     User.orders.and_(Order.description != "somename")
                 ),
                 Load(User).defer(User.id),
-                Load(User).subqueryload("addresses"),
-                Load(Address).defer("id"),
+                Load(User).subqueryload(User.addresses),
+                Load(Address).defer(Address.id),
                 Load(Address).defer("*"),
-                Load(aliased(Address)).defer("id"),
+                Load(a1).defer(a1.id),
                 Load(User).joinedload(User.addresses).defer(Address.id),
                 Load(User).joinedload(User.orders).joinedload(Order.items),
                 Load(User).joinedload(User.orders).subqueryload(Order.items),
@@ -276,33 +278,11 @@ class CacheKeyTest(CacheKeyFixture, _fixtures.FixtureTest):
                 Load(User).defaultload(User.orders).defaultload(Order.items),
                 Load(User).defaultload(User.orders),
                 Load(Address).raiseload("*"),
-                Load(Address).raiseload("user"),
+                Load(Address).raiseload(Address.user),
             ),
             compare_values=True,
         )
 
-    def test_bound_options_equiv_on_strname(self):
-        """Bound loader options resolve on string name so test that the cache
-        key for the string version matches the resolved version.
-
-        """
-        User, Address, Keyword, Order, Item = self.classes(
-            "User", "Address", "Keyword", "Order", "Item"
-        )
-
-        for left, right in [
-            (Load(User).defer(User.id), Load(User).defer("id")),
-            (
-                Load(User).joinedload(User.addresses),
-                Load(User).joinedload("addresses"),
-            ),
-            (
-                Load(User).joinedload(User.orders).joinedload(Order.items),
-                Load(User).joinedload("orders").joinedload("items"),
-            ),
-        ]:
-            eq_(left._generate_cache_key(), right._generate_cache_key())
-
     def test_selects_w_orm_joins(self):
 
         User, Address, Keyword, Order, Item = self.classes(
@@ -353,16 +333,10 @@ class CacheKeyTest(CacheKeyFixture, _fixtures.FixtureTest):
                 .join(User.orders),
                 fixture_session()
                 .query(User)
-                .join("addresses")
-                .join("dingalings", from_joinpoint=True),
-                fixture_session().query(User).join("addresses"),
-                fixture_session().query(User).join("orders"),
-                fixture_session().query(User).join("addresses").join("orders"),
+                .join(User.addresses)
+                .join(Address.dingaling),
                 fixture_session().query(User).join(Address, User.addresses),
-                fixture_session().query(User).join(a1, "addresses"),
-                fixture_session()
-                .query(User)
-                .join(a1, "addresses", aliased=True),
+                fixture_session().query(User).join(a1, User.addresses),
                 fixture_session().query(User).join(User.addresses.of_type(a1)),
             ),
             compare_values=True,
index 4bdca7a45ac90c4e639f6b2468f1e34d97791f71..1dc139df98dc7e0d6dc04f85e8bed420f3bb1cc9 100644 (file)
@@ -179,7 +179,9 @@ class PointTest(fixtures.MappedTest, testing.AssertsCompiledSQL):
 
         def go():
             g2 = (
-                sess.query(Graph).options(sa.orm.joinedload("edges")).get(g.id)
+                sess.query(Graph)
+                .options(sa.orm.joinedload(Graph.edges))
+                .get(g.id)
             )
 
             eq_(
index 249a446ea7467ace687e51aa053275dacf606317..9b228bbaa2540ff9a98cc1cf13d63ecacb92123a 100644 (file)
@@ -1,7 +1,10 @@
 import sqlalchemy as sa
 from sqlalchemy import testing
 from sqlalchemy import util
+from sqlalchemy.orm import defaultload
+from sqlalchemy.orm import joinedload
 from sqlalchemy.orm import relationship
+from sqlalchemy.orm import subqueryload
 from sqlalchemy.testing import assert_raises_message
 from sqlalchemy.testing import eq_
 from sqlalchemy.testing.fixtures import fixture_session
@@ -239,8 +242,8 @@ class DefaultStrategyOptionsTest(_fixtures.FixtureTest):
         def go():
             users[:] = (
                 sess.query(self.classes.User)
-                .options(sa.orm.subqueryload("*"))
-                .options(sa.orm.joinedload(self.classes.User.addresses))
+                .options(subqueryload("*"))
+                .options(joinedload(self.classes.User.addresses))
                 .options(sa.orm.lazyload("*"))
                 .order_by(self.classes.User.id)
                 .all()
@@ -254,7 +257,7 @@ class DefaultStrategyOptionsTest(_fixtures.FixtureTest):
     def test_star_must_be_alone(self):
         sess = self._downgrade_fixture()
         User = self.classes.User
-        opt = sa.orm.subqueryload("*", User.addresses)
+        opt = subqueryload("*", User.addresses)
         assert_raises_message(
             sa.exc.ArgumentError,
             "Wildcard token cannot be followed by another entity",
@@ -287,7 +290,7 @@ class DefaultStrategyOptionsTest(_fixtures.FixtureTest):
             users[:] = (
                 sess.query(self.classes.User)
                 .options(sa.orm.lazyload("*"))
-                .options(sa.orm.joinedload(self.classes.User.addresses))
+                .options(joinedload(self.classes.User.addresses))
                 .order_by(self.classes.User.id)
                 .all()
             )
@@ -317,7 +320,7 @@ class DefaultStrategyOptionsTest(_fixtures.FixtureTest):
             users[:] = (
                 sess.query(self.classes.User)
                 .options(sa.orm.lazyload("*"))
-                .options(sa.orm.subqueryload(self.classes.User.orders))
+                .options(subqueryload(self.classes.User.orders))
                 .order_by(self.classes.User.id)
                 .all()
             )
@@ -355,7 +358,7 @@ class DefaultStrategyOptionsTest(_fixtures.FixtureTest):
             users[:] = (
                 sess.query(self.classes.User)
                 .options(sa.orm.noload("*"))
-                .options(sa.orm.joinedload(self.classes.User.addresses))
+                .options(joinedload(self.classes.User.addresses))
                 .order_by(self.classes.User.id)
                 .all()
             )
@@ -385,7 +388,7 @@ class DefaultStrategyOptionsTest(_fixtures.FixtureTest):
             users[:] = (
                 sess.query(self.classes.User)
                 .options(sa.orm.noload("*"))
-                .options(sa.orm.subqueryload(self.classes.User.orders))
+                .options(subqueryload(self.classes.User.orders))
                 .order_by(self.classes.User.id)
                 .all()
             )
@@ -414,7 +417,7 @@ class DefaultStrategyOptionsTest(_fixtures.FixtureTest):
         def go():
             users[:] = (
                 sess.query(self.classes.User)
-                .options(sa.orm.joinedload("*"))
+                .options(joinedload("*"))
                 .order_by(self.classes.User.id)
                 .all()
             )
@@ -428,14 +431,20 @@ class DefaultStrategyOptionsTest(_fixtures.FixtureTest):
         sess = self._upgrade_fixture()
         users = []
 
+        User, Order, Item = self.classes("User", "Order", "Item")
+
         # test upgrade all to joined: 1 sql
         def go():
             users[:] = (
-                sess.query(self.classes.User)
-                .options(sa.orm.joinedload(".*"))
-                .options(sa.orm.joinedload("addresses.*"))
-                .options(sa.orm.joinedload("orders.*"))
-                .options(sa.orm.joinedload("orders.items.*"))
+                sess.query(User)
+                .options(joinedload(".*"))
+                .options(defaultload(User.addresses).joinedload("*"))
+                .options(defaultload(User.orders).joinedload("*"))
+                .options(
+                    defaultload(User.orders)
+                    .defaultload(Order.items)
+                    .joinedload("*")
+                )
                 .order_by(self.classes.User.id)
                 .all()
             )
@@ -450,13 +459,19 @@ class DefaultStrategyOptionsTest(_fixtures.FixtureTest):
         sess = self._upgrade_fixture()
         users = []
 
+        User, Order, Item = self.classes("User", "Order", "Item")
+
         # test joined all but 'keywords': upgraded to 1 sql
         def go():
             users[:] = (
-                sess.query(self.classes.User)
-                .options(sa.orm.lazyload("orders.items.keywords"))
-                .options(sa.orm.joinedload("*"))
-                .order_by(self.classes.User.id)
+                sess.query(User)
+                .options(
+                    defaultload(User.orders)
+                    .defaultload(Order.items)
+                    .lazyload(Item.keywords)
+                )
+                .options(joinedload("*"))
+                .order_by(User.id)
                 .all()
             )
 
@@ -492,8 +507,8 @@ class DefaultStrategyOptionsTest(_fixtures.FixtureTest):
         def go():
             users[:] = (
                 sess.query(self.classes.User)
-                .options(sa.orm.subqueryload(self.classes.User.addresses))
-                .options(sa.orm.joinedload("*"))
+                .options(subqueryload(self.classes.User.addresses))
+                .options(joinedload("*"))
                 .order_by(self.classes.User.id)
                 .all()
             )
@@ -513,7 +528,7 @@ class DefaultStrategyOptionsTest(_fixtures.FixtureTest):
         def go():
             users[:] = (
                 sess.query(self.classes.User)
-                .options(sa.orm.subqueryload("*"))
+                .options(subqueryload("*"))
                 .order_by(self.classes.User.id)
                 .all()
             )
@@ -527,15 +542,21 @@ class DefaultStrategyOptionsTest(_fixtures.FixtureTest):
         sess = self._upgrade_fixture()
         users = []
 
+        User, Order = self.classes("User", "Order")
+
         # test upgrade all to subquery: 1 sql + 4 relationships = 5
         def go():
             users[:] = (
-                sess.query(self.classes.User)
-                .options(sa.orm.subqueryload(".*"))
-                .options(sa.orm.subqueryload("addresses.*"))
-                .options(sa.orm.subqueryload("orders.*"))
-                .options(sa.orm.subqueryload("orders.items.*"))
-                .order_by(self.classes.User.id)
+                sess.query(User)
+                .options(subqueryload(".*"))
+                .options(defaultload(User.addresses).subqueryload("*"))
+                .options(defaultload(User.orders).subqueryload("*"))
+                .options(
+                    defaultload(User.orders)
+                    .defaultload(Order.items)
+                    .subqueryload("*")
+                )
+                .order_by(User.id)
                 .all()
             )
 
@@ -550,14 +571,19 @@ class DefaultStrategyOptionsTest(_fixtures.FixtureTest):
         is still honored"""
         sess = self._upgrade_fixture()
         users = []
+        User, Order, Item = self.classes("User", "Order", "Item")
 
         # test subquery all but 'keywords' (1 sql + 3 relationships = 4)
         def go():
             users[:] = (
-                sess.query(self.classes.User)
-                .options(sa.orm.lazyload("orders.items.keywords"))
-                .options(sa.orm.subqueryload("*"))
-                .order_by(self.classes.User.id)
+                sess.query(User)
+                .options(
+                    defaultload(User.orders)
+                    .defaultload(Order.items)
+                    .lazyload(Item.keywords)
+                )
+                .options(subqueryload("*"))
+                .order_by(User.id)
                 .all()
             )
 
@@ -593,9 +619,9 @@ class DefaultStrategyOptionsTest(_fixtures.FixtureTest):
         def go():
             users[:] = (
                 sess.query(self.classes.User)
-                .options(sa.orm.joinedload(self.classes.User.addresses))
-                .options(sa.orm.joinedload(self.classes.User.orders))
-                .options(sa.orm.subqueryload("*"))
+                .options(joinedload(self.classes.User.addresses))
+                .options(joinedload(self.classes.User.orders))
+                .options(subqueryload("*"))
                 .order_by(self.classes.User.id)
                 .all()
             )
@@ -661,7 +687,7 @@ class NoLoadTest(_fixtures.FixtureTest):
                 )
             ),
         )
-        q = fixture_session().query(m).options(sa.orm.lazyload("addresses"))
+        q = fixture_session().query(m).options(sa.orm.lazyload(User.addresses))
         result = [None]
 
         def go():
@@ -690,7 +716,7 @@ class NoLoadTest(_fixtures.FixtureTest):
         a1 = (
             s.query(Address)
             .filter_by(id=1)
-            .options(sa.orm.noload("user"))
+            .options(sa.orm.noload(Address.user))
             .first()
         )
 
index 9f9068783faaf6739935cb3f4fdf82338fa46304..e44d6ec8690d494e59b375390a1a6ed14d719bd7 100644 (file)
@@ -370,7 +370,7 @@ class DeferredOptionsTest(AssertsCompiledSQL, _fixtures.FixtureTest):
         self.mapper_registry.map_imperatively(Order, orders)
 
         sess = fixture_session()
-        q = sess.query(Order).order_by(Order.id).options(defer("user_id"))
+        q = sess.query(Order).order_by(Order.id).options(defer(Order.user_id))
 
         def go():
             q.all()[0].user_id
@@ -395,7 +395,7 @@ class DeferredOptionsTest(AssertsCompiledSQL, _fixtures.FixtureTest):
         )
         sess.expunge_all()
 
-        q2 = q.options(undefer("user_id"))
+        q2 = q.options(undefer(Order.user_id))
         self.sql_eq_(
             q2.all,
             [
@@ -985,7 +985,11 @@ class DeferredOptionsTest(AssertsCompiledSQL, _fixtures.FixtureTest):
         eq_(item.description, "item 4")
 
         sess.expunge_all()
-        result = q.options(undefer("orders.items.description")).all()
+        result = q.options(
+            defaultload(User.orders)
+            .defaultload(Order.items)
+            .undefer(Item.description)
+        ).all()
         item = result[0].orders[1].items[1]
 
         def go():
@@ -1055,7 +1059,9 @@ class DeferredOptionsTest(AssertsCompiledSQL, _fixtures.FixtureTest):
 
         sess = fixture_session()
         q = sess.query(User).options(
-            joinedload(User.orders).defer("description").defer("isopen")
+            joinedload(User.orders)
+            .defer(Order.description)
+            .defer(Order.isopen)
         )
         self.assert_compile(
             q,
@@ -1075,7 +1081,9 @@ class DeferredOptionsTest(AssertsCompiledSQL, _fixtures.FixtureTest):
         self.mapper_registry.map_imperatively(Order, orders)
 
         sess = fixture_session()
-        q = sess.query(Order).options(load_only("isopen", "description"))
+        q = sess.query(Order).options(
+            load_only(Order.isopen, Order.description)
+        )
         self.assert_compile(
             q,
             "SELECT orders.id AS orders_id, "
@@ -1092,7 +1100,7 @@ class DeferredOptionsTest(AssertsCompiledSQL, _fixtures.FixtureTest):
         q = (
             sess.query(Order)
             .order_by(Order.id)
-            .options(load_only("isopen", "description"))
+            .options(load_only(Order.isopen, Order.description))
         )
         eq_(q.first(), Order(id=1))
 
@@ -1107,7 +1115,7 @@ class DeferredOptionsTest(AssertsCompiledSQL, _fixtures.FixtureTest):
 
         sess = fixture_session()
         q = sess.query(Order).options(
-            load_only("isopen", "description"), undefer("user_id")
+            load_only(Order.isopen, Order.description), undefer(Order.user_id)
         )
         self.assert_compile(
             q,
@@ -1117,8 +1125,7 @@ class DeferredOptionsTest(AssertsCompiledSQL, _fixtures.FixtureTest):
             "orders.isopen AS orders_isopen FROM orders",
         )
 
-    @testing.combinations(("string",), ("attr",))
-    def test_load_only_synonym(self, type_):
+    def test_load_only_synonym(self):
         orders, Order = self.tables.orders, self.classes.Order
 
         self.mapper_registry.map_imperatively(
@@ -1127,10 +1134,7 @@ class DeferredOptionsTest(AssertsCompiledSQL, _fixtures.FixtureTest):
             properties={"desc": synonym("description")},
         )
 
-        if type_ == "attr":
-            opt = load_only(Order.isopen, Order.desc)
-        else:
-            opt = load_only("isopen", "desc")
+        opt = load_only(Order.isopen, Order.desc)
 
         sess = fixture_session()
         q = sess.query(Order).options(opt)
@@ -1184,10 +1188,12 @@ class DeferredOptionsTest(AssertsCompiledSQL, _fixtures.FixtureTest):
             opt = (
                 Load(User)
                 .defaultload(User.addresses)
-                .load_only("id", "email_address")
+                .load_only(Address.id, Address.email_address)
             )
         else:
-            opt = defaultload(User.addresses).load_only("id", "email_address")
+            opt = defaultload(User.addresses).load_only(
+                Address.id, Address.email_address
+            )
         q = sess.query(User).options(opt).filter(User.id.in_([7, 8]))
 
         def go():
@@ -1211,9 +1217,9 @@ class DeferredOptionsTest(AssertsCompiledSQL, _fixtures.FixtureTest):
 
         sess = fixture_session()
         q = sess.query(User, Order, Address).options(
-            Load(User).load_only("name"),
-            Load(Order).load_only("id"),
-            Load(Address).load_only("id", "email_address"),
+            Load(User).load_only(User.name),
+            Load(Order).load_only(Order.id),
+            Load(Address).load_only(Address.id, Address.email_address),
         )
 
         self.assert_compile(
@@ -1252,10 +1258,10 @@ class DeferredOptionsTest(AssertsCompiledSQL, _fixtures.FixtureTest):
         sess = fixture_session()
 
         q = sess.query(User).options(
-            load_only("name")
-            .defaultload("addresses")
-            .load_only("id", "email_address"),
-            defaultload("orders").load_only("id"),
+            load_only(User.name)
+            .defaultload(User.addresses)
+            .load_only(Address.id, Address.email_address),
+            defaultload(User.orders).load_only(Order.id),
         )
 
         # hmmmm joinedload seems to be forcing users.id into here...
@@ -1339,7 +1345,7 @@ class InheritanceTest(_Polymorphic):
         q = (
             s.query(Manager)
             .order_by(Manager.person_id)
-            .options(load_only("status", "manager_name"))
+            .options(load_only(Manager.status, Manager.manager_name))
         )
         self.assert_compile(
             q,
@@ -1358,7 +1364,9 @@ class InheritanceTest(_Polymorphic):
         q = (
             s.query(Manager)
             .order_by(Manager.person_id)
-            .options(Load(Manager).load_only("status", "manager_name"))
+            .options(
+                Load(Manager).load_only(Manager.status, Manager.manager_name)
+            )
         )
         self.assert_compile(
             q,
@@ -1377,7 +1385,7 @@ class InheritanceTest(_Polymorphic):
         q = (
             s.query(Boss)
             .order_by(Person.person_id)
-            .options(load_only("status", "manager_name"))
+            .options(load_only(Boss.status, Boss.manager_name))
         )
         self.assert_compile(
             q,
@@ -1396,7 +1404,7 @@ class InheritanceTest(_Polymorphic):
         q = (
             s.query(Boss)
             .order_by(Person.person_id)
-            .options(Load(Boss).load_only("status", "manager_name"))
+            .options(Load(Boss).load_only(Boss.status, Manager.manager_name))
         )
         self.assert_compile(
             q,
@@ -1416,7 +1424,7 @@ class InheritanceTest(_Polymorphic):
         q = (
             s.query(m1)
             .order_by(m1.person_id)
-            .options(load_only("status", "manager_name"))
+            .options(load_only(m1.status, m1.manager_name))
         )
         self.assert_compile(
             q,
@@ -1436,7 +1444,7 @@ class InheritanceTest(_Polymorphic):
         q = (
             s.query(m1)
             .order_by(m1.person_id)
-            .options(Load(m1).load_only("status", "manager_name"))
+            .options(Load(m1).load_only(m1.status, m1.manager_name))
         )
         self.assert_compile(
             q,
@@ -1511,7 +1519,7 @@ class InheritanceTest(_Polymorphic):
             .join(Company.managers)
             .options(
                 contains_eager(Company.managers).load_only(
-                    "status", "manager_name"
+                    Manager.status, Manager.manager_name
                 )
             )
         )
@@ -1536,7 +1544,7 @@ class InheritanceTest(_Polymorphic):
             .options(
                 Load(Company)
                 .contains_eager(Company.managers)
-                .load_only("status", "manager_name")
+                .load_only(Manager.status, Manager.manager_name)
             )
         )
         self.assert_compile(
@@ -1562,7 +1570,7 @@ class InheritanceTest(_Polymorphic):
         q = (
             s.query(Manager)
             .order_by(Person.person_id)
-            .options(defer(".*"), undefer("status"))
+            .options(defer(".*"), undefer(Manager.status))
         )
         self.assert_compile(
             q,
@@ -1577,7 +1585,9 @@ class InheritanceTest(_Polymorphic):
     def test_load_only_subclass_of_type(self):
         s = fixture_session()
         q = s.query(Company).options(
-            joinedload(Company.employees.of_type(Manager)).load_only("status")
+            joinedload(Company.employees.of_type(Manager)).load_only(
+                Manager.status
+            )
         )
         self.assert_compile(
             q,
@@ -1624,7 +1634,11 @@ class InheritanceTest(_Polymorphic):
 
     def test_defer_super_name_on_subclass(self):
         s = fixture_session()
-        q = s.query(Manager).order_by(Person.person_id).options(defer("name"))
+        q = (
+            s.query(Manager)
+            .order_by(Person.person_id)
+            .options(defer(Person.name))
+        )
         self.assert_compile(
             q,
             "SELECT managers.person_id AS managers_person_id, "
@@ -1642,7 +1656,7 @@ class InheritanceTest(_Polymorphic):
         q = (
             s.query(Manager)
             .order_by(Person.person_id)
-            .options(Load(Manager).defer("name"))
+            .options(Load(Manager).defer(Manager.name))
         )
         self.assert_compile(
             q,
@@ -1686,19 +1700,6 @@ class InheritanceTest(_Polymorphic):
 
         wp = with_polymorphic(Person, [Manager], flat=True)
 
-        # needs to be explicit, we don't currently dig onto all the
-        # sub-entities in the wp
-        assert_raises_message(
-            sa.exc.ArgumentError,
-            r'Can\'t find property named "status" on '
-            r"with_polymorphic\(Person, \[Manager\]\) in this Query.",
-            s.query(Company)
-            .options(
-                joinedload(Company.employees.of_type(wp)).load_only("status")
-            )
-            ._compile_context,
-        )
-
         assert_raises_message(
             sa.exc.ArgumentError,
             'Attribute "Manager.status" does not link from element '
@@ -2225,7 +2226,9 @@ class DeferredPopulationTest(fixtures.MappedTest):
         Thing = self.classes.Thing
 
         session = fixture_session()
-        thing = session.query(Thing).options(sa.orm.undefer("name")).first()
+        thing = (
+            session.query(Thing).options(sa.orm.undefer(Thing.name)).first()
+        )
         self._test(thing)
 
     def test_query_twice_with_clear(self):
@@ -2234,7 +2237,9 @@ class DeferredPopulationTest(fixtures.MappedTest):
         session = fixture_session()
         result = session.query(Thing).first()  # noqa
         session.expunge_all()
-        thing = session.query(Thing).options(sa.orm.undefer("name")).first()
+        thing = (
+            session.query(Thing).options(sa.orm.undefer(Thing.name)).first()
+        )
         self._test(thing)
 
     def test_query_twice_no_clear(self):
@@ -2242,7 +2247,9 @@ class DeferredPopulationTest(fixtures.MappedTest):
 
         session = fixture_session()
         result = session.query(Thing).first()  # noqa
-        thing = session.query(Thing).options(sa.orm.undefer("name")).first()
+        thing = (
+            session.query(Thing).options(sa.orm.undefer(Thing.name)).first()
+        )
         self._test(thing)
 
     def test_joinedload_with_clear(self):
@@ -2250,10 +2257,14 @@ class DeferredPopulationTest(fixtures.MappedTest):
 
         session = fixture_session()
         human = (  # noqa
-            session.query(Human).options(sa.orm.joinedload("thing")).first()
+            session.query(Human)
+            .options(sa.orm.joinedload(Human.thing))
+            .first()
         )
         session.expunge_all()
-        thing = session.query(Thing).options(sa.orm.undefer("name")).first()
+        thing = (
+            session.query(Thing).options(sa.orm.undefer(Thing.name)).first()
+        )
         self._test(thing)
 
     def test_joinedload_no_clear(self):
@@ -2261,9 +2272,13 @@ class DeferredPopulationTest(fixtures.MappedTest):
 
         session = fixture_session()
         human = (  # noqa
-            session.query(Human).options(sa.orm.joinedload("thing")).first()
+            session.query(Human)
+            .options(sa.orm.joinedload(Human.thing))
+            .first()
+        )
+        thing = (
+            session.query(Thing).options(sa.orm.undefer(Thing.name)).first()
         )
-        thing = session.query(Thing).options(sa.orm.undefer("name")).first()
         self._test(thing)
 
     def test_join_with_clear(self):
@@ -2271,10 +2286,12 @@ class DeferredPopulationTest(fixtures.MappedTest):
 
         session = fixture_session()
         result = (  # noqa
-            session.query(Human).add_entity(Thing).join("thing").first()
+            session.query(Human).add_entity(Thing).join(Human.thing).first()
         )
         session.expunge_all()
-        thing = session.query(Thing).options(sa.orm.undefer("name")).first()
+        thing = (
+            session.query(Thing).options(sa.orm.undefer(Thing.name)).first()
+        )
         self._test(thing)
 
     def test_join_no_clear(self):
@@ -2282,7 +2299,9 @@ class DeferredPopulationTest(fixtures.MappedTest):
 
         session = fixture_session()
         result = (  # noqa
-            session.query(Human).add_entity(Thing).join("thing").first()
+            session.query(Human).add_entity(Thing).join(Human.thing).first()
+        )
+        thing = (
+            session.query(Thing).options(sa.orm.undefer(Thing.name)).first()
         )
-        thing = session.query(Thing).options(sa.orm.undefer("name")).first()
         self._test(thing)
index ddfad7e0f0d47dd9e5e96119d9040abb21a1e0fb..2ab45827b5e465b36ff32c1f78acff3c26cc492d 100644 (file)
@@ -7,6 +7,7 @@ from sqlalchemy import event
 from sqlalchemy import exc as sa_exc
 from sqlalchemy import ForeignKey
 from sqlalchemy import func
+from sqlalchemy import inspect
 from sqlalchemy import Integer
 from sqlalchemy import literal_column
 from sqlalchemy import MetaData
@@ -31,18 +32,24 @@ from sqlalchemy.orm import contains_alias
 from sqlalchemy.orm import contains_eager
 from sqlalchemy.orm import declarative_base
 from sqlalchemy.orm import declared_attr
+from sqlalchemy.orm import defaultload
 from sqlalchemy.orm import defer
 from sqlalchemy.orm import deferred
 from sqlalchemy.orm import eagerload
+from sqlalchemy.orm import exc as orm_exc
 from sqlalchemy.orm import foreign
 from sqlalchemy.orm import instrumentation
 from sqlalchemy.orm import joinedload
+from sqlalchemy.orm import Load
+from sqlalchemy.orm import load_only
 from sqlalchemy.orm import mapper
 from sqlalchemy.orm import relation
 from sqlalchemy.orm import relationship
 from sqlalchemy.orm import scoped_session
+from sqlalchemy.orm import selectinload
 from sqlalchemy.orm import Session
 from sqlalchemy.orm import sessionmaker
+from sqlalchemy.orm import strategy_options
 from sqlalchemy.orm import subqueryload
 from sqlalchemy.orm import synonym
 from sqlalchemy.orm import undefer
@@ -70,14 +77,22 @@ from sqlalchemy.testing.mock import call
 from sqlalchemy.testing.mock import Mock
 from sqlalchemy.testing.schema import Column
 from sqlalchemy.testing.schema import Table
+from sqlalchemy.util import pickle
 from . import _fixtures
 from .inheritance import _poly_fixtures
+from .inheritance._poly_fixtures import _Polymorphic
+from .inheritance._poly_fixtures import Company
+from .inheritance._poly_fixtures import Engineer
+from .test_ac_relationships import PartitionByFixture
 from .test_bind import GetBindTest as _GetBindTest
 from .test_dynamic import _DynamicFixture
 from .test_events import _RemoveListeners
 from .test_options import PathTest as OptionsPathTest
+from .test_options import PathTest
+from .test_options import QueryTest as OptionsQueryTest
 from .test_query import QueryTest
 from .test_transaction import _LocalFixture
+from ..sql.test_compare import CacheKeyFixture
 
 
 join_aliased_dep = (
@@ -94,12 +109,27 @@ join_chain_dep = (
     r"Passing a chain of multiple join conditions to Query.join\(\)"
 )
 
+undefer_needs_chaining = (
+    r"The \*addl_attrs on orm.(?:un)?defer is deprecated.  "
+    "Please use method chaining"
+)
+
 join_strings_dep = "Using strings to indicate relationship names in Query.join"
 join_tuple_form = (
     r"Query.join\(\) will no longer accept tuples as "
     "arguments in SQLAlchemy 2.0."
 )
 
+opt_strings_dep = (
+    "Using strings to indicate column or relationship "
+    "paths in loader options"
+)
+
+wparent_strings_dep = (
+    r"Using strings to indicate relationship names "
+    r"in the ORM with_parent\(\) function"
+)
+
 
 def _aliased_join_warning(arg=None):
     return testing.expect_warnings(
@@ -115,6 +145,337 @@ def _aliased_join_deprecation(arg=None):
     )
 
 
+class CustomJoinTest(QueryTest):
+    run_setup_mappers = None
+
+    def test_double_same_mappers_flag_alias(self):
+        """test aliasing of joins with a custom join condition"""
+
+        (
+            addresses,
+            items,
+            order_items,
+            orders,
+            Item,
+            User,
+            Address,
+            Order,
+            users,
+        ) = (
+            self.tables.addresses,
+            self.tables.items,
+            self.tables.order_items,
+            self.tables.orders,
+            self.classes.Item,
+            self.classes.User,
+            self.classes.Address,
+            self.classes.Order,
+            self.tables.users,
+        )
+
+        self.mapper_registry.map_imperatively(Address, addresses)
+        self.mapper_registry.map_imperatively(
+            Order,
+            orders,
+            properties={
+                "items": relationship(
+                    Item,
+                    secondary=order_items,
+                    lazy="select",
+                    order_by=items.c.id,
+                )
+            },
+        )
+        self.mapper_registry.map_imperatively(Item, items)
+        self.mapper_registry.map_imperatively(
+            User,
+            users,
+            properties=dict(
+                addresses=relationship(Address, lazy="select"),
+                open_orders=relationship(
+                    Order,
+                    primaryjoin=and_(
+                        orders.c.isopen == 1, users.c.id == orders.c.user_id
+                    ),
+                    lazy="select",
+                    viewonly=True,
+                ),
+                closed_orders=relationship(
+                    Order,
+                    primaryjoin=and_(
+                        orders.c.isopen == 0, users.c.id == orders.c.user_id
+                    ),
+                    lazy="select",
+                    viewonly=True,
+                ),
+            ),
+        )
+        q = fixture_session().query(User)
+
+        with assertions.expect_deprecated_20(
+            join_aliased_dep,
+            join_strings_dep,
+            join_chain_dep,
+            raise_on_any_unexpected=True,
+        ):
+            eq_(
+                q.join("open_orders", "items", aliased=True)
+                .filter(Item.id == 4)
+                .join("closed_orders", "items", aliased=True)
+                .filter(Item.id == 3)
+                .all(),
+                [User(id=7)],
+            )
+
+
+class PickleTest(fixtures.MappedTest):
+    @classmethod
+    def define_tables(cls, metadata):
+        Table(
+            "users",
+            metadata,
+            Column(
+                "id", Integer, primary_key=True, test_needs_autoincrement=True
+            ),
+            Column("name", String(30), nullable=False),
+            test_needs_acid=True,
+            test_needs_fk=True,
+        )
+
+        Table(
+            "addresses",
+            metadata,
+            Column(
+                "id", Integer, primary_key=True, test_needs_autoincrement=True
+            ),
+            Column("user_id", None, ForeignKey("users.id")),
+            Column("email_address", String(50), nullable=False),
+            test_needs_acid=True,
+            test_needs_fk=True,
+        )
+        Table(
+            "orders",
+            metadata,
+            Column(
+                "id", Integer, primary_key=True, test_needs_autoincrement=True
+            ),
+            Column("user_id", None, ForeignKey("users.id")),
+            Column("address_id", None, ForeignKey("addresses.id")),
+            Column("description", String(30)),
+            Column("isopen", Integer),
+            test_needs_acid=True,
+            test_needs_fk=True,
+        )
+        Table(
+            "dingalings",
+            metadata,
+            Column(
+                "id", Integer, primary_key=True, test_needs_autoincrement=True
+            ),
+            Column("address_id", None, ForeignKey("addresses.id")),
+            Column("data", String(30)),
+            test_needs_acid=True,
+            test_needs_fk=True,
+        )
+
+    def _option_test_fixture(self):
+        users, addresses, dingalings = (
+            self.tables.users,
+            self.tables.addresses,
+            self.tables.dingalings,
+        )
+
+        # these must be module level for pickling
+        from .test_pickled import User, Address, Dingaling
+
+        self.mapper_registry.map_imperatively(
+            User,
+            users,
+            properties={"addresses": relationship(Address, backref="user")},
+        )
+        self.mapper_registry.map_imperatively(
+            Address,
+            addresses,
+            properties={"dingaling": relationship(Dingaling)},
+        )
+        self.mapper_registry.map_imperatively(Dingaling, dingalings)
+        sess = fixture_session()
+        u1 = User(name="ed")
+        u1.addresses.append(Address(email_address="ed@bar.com"))
+        sess.add(u1)
+        sess.flush()
+        sess.expunge_all()
+        return sess, User, Address, Dingaling
+
+    @testing.requires.non_broken_pickle
+    def test_became_bound_options(self):
+        sess, User, Address, Dingaling = self._option_test_fixture()
+
+        for opt in [
+            sa.orm.joinedload("addresses"),
+            sa.orm.defer("name"),
+            sa.orm.joinedload("addresses").joinedload(Address.dingaling),
+        ]:
+            with assertions.expect_deprecated_20(opt_strings_dep):
+                context = sess.query(User).options(opt)._compile_context()
+            opt = [
+                v
+                for v in context.attributes.values()
+                if isinstance(v, sa.orm.Load)
+            ][0]
+
+            opt2 = pickle.loads(pickle.dumps(opt))
+            eq_(opt.path, opt2.path)
+            eq_(opt.local_opts, opt2.local_opts)
+
+        u1 = sess.query(User).options(opt).first()
+        pickle.loads(pickle.dumps(u1))
+
+    @testing.requires.non_broken_pickle
+    @testing.combinations(
+        lambda: sa.orm.joinedload("addresses"),
+        lambda: sa.orm.defer("name"),
+        lambda Address: sa.orm.joinedload("addresses").joinedload(
+            Address.dingaling
+        ),
+        lambda: sa.orm.joinedload("addresses").raiseload("*"),
+    )
+    def test_unbound_options(self, test_case):
+        sess, User, Address, Dingaling = self._option_test_fixture()
+
+        opt = testing.resolve_lambda(test_case, User=User, Address=Address)
+        opt2 = pickle.loads(pickle.dumps(opt))
+        eq_(opt.path, opt2.path)
+
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            u1 = sess.query(User).options(opt).first()
+        pickle.loads(pickle.dumps(u1))
+
+    @testing.requires.non_broken_pickle
+    @testing.combinations(
+        lambda User: sa.orm.Load(User).joinedload("addresses"),
+        lambda User: sa.orm.Load(User).joinedload("addresses").raiseload("*"),
+        lambda User: sa.orm.Load(User).defer("name"),
+        lambda User, Address: sa.orm.Load(User)
+        .joinedload("addresses")
+        .joinedload(Address.dingaling),
+        lambda User, Address: sa.orm.Load(User)
+        .joinedload("addresses", innerjoin=True)
+        .joinedload(Address.dingaling),
+    )
+    def test_bound_options(self, test_case):
+        sess, User, Address, Dingaling = self._option_test_fixture()
+
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            opt = testing.resolve_lambda(test_case, User=User, Address=Address)
+
+        opt2 = pickle.loads(pickle.dumps(opt))
+        eq_(opt.path, opt2.path)
+        eq_(opt.context.keys(), opt2.context.keys())
+        eq_(opt.local_opts, opt2.local_opts)
+
+        u1 = sess.query(User).options(opt).first()
+        pickle.loads(pickle.dumps(u1))
+
+
+class SynonymTest(QueryTest, AssertsCompiledSQL):
+    __dialect__ = "default"
+
+    @classmethod
+    def setup_mappers(cls):
+        (
+            users,
+            Keyword,
+            items,
+            order_items,
+            orders,
+            Item,
+            User,
+            Address,
+            keywords,
+            Order,
+            item_keywords,
+            addresses,
+        ) = (
+            cls.tables.users,
+            cls.classes.Keyword,
+            cls.tables.items,
+            cls.tables.order_items,
+            cls.tables.orders,
+            cls.classes.Item,
+            cls.classes.User,
+            cls.classes.Address,
+            cls.tables.keywords,
+            cls.classes.Order,
+            cls.tables.item_keywords,
+            cls.tables.addresses,
+        )
+
+        cls.mapper_registry.map_imperatively(
+            User,
+            users,
+            properties={
+                "name_syn": synonym("name"),
+                "addresses": relationship(Address),
+                "orders": relationship(
+                    Order, backref="user", order_by=orders.c.id
+                ),  # o2m, m2o
+                "orders_syn": synonym("orders"),
+                "orders_syn_2": synonym("orders_syn"),
+            },
+        )
+        cls.mapper_registry.map_imperatively(Address, addresses)
+        cls.mapper_registry.map_imperatively(
+            Order,
+            orders,
+            properties={
+                "items": relationship(Item, secondary=order_items),  # m2m
+                "address": relationship(Address),  # m2o
+                "items_syn": synonym("items"),
+            },
+        )
+        cls.mapper_registry.map_imperatively(
+            Item,
+            items,
+            properties={
+                "keywords": relationship(
+                    Keyword, secondary=item_keywords
+                )  # m2m
+            },
+        )
+        cls.mapper_registry.map_imperatively(Keyword, keywords)
+
+    def test_options_syn_of_syn_string(self):
+        User, Order = self.classes.User, self.classes.Order
+
+        s = fixture_session()
+
+        def go():
+            with testing.expect_deprecated_20(opt_strings_dep):
+                result = (
+                    s.query(User)
+                    .filter_by(name="jack")
+                    .options(joinedload("orders_syn_2"))
+                    .all()
+                )
+            eq_(
+                result,
+                [
+                    User(
+                        id=7,
+                        name="jack",
+                        orders=[
+                            Order(description="order 1"),
+                            Order(description="order 3"),
+                            Order(description="order 5"),
+                        ],
+                    )
+                ],
+            )
+
+        self.assert_sql_count(testing.db, go, 1)
+
+
 class DeprecatedQueryTest(_fixtures.FixtureTest, AssertsCompiledSQL):
     __dialect__ = "default"
 
@@ -310,10 +671,7 @@ class DeprecatedQueryTest(_fixtures.FixtureTest, AssertsCompiledSQL):
 
         q1 = s.query(User).options(joinedload("addresses"))
 
-        with testing.expect_deprecated_20(
-            "Using strings to indicate column or relationship "
-            "paths in loader options"
-        ):
+        with testing.expect_deprecated_20(opt_strings_dep):
             self.assert_compile(
                 q1,
                 "SELECT users.id AS users_id, users.name AS users_name, "
@@ -2754,16 +3112,10 @@ class DeprecatedOptionAllTest(OptionsPathTest, _fixtures.FixtureTest):
 
         sess = fixture_session()
 
-        with testing.expect_deprecated(
-            r"The \*addl_attrs on orm.defer is deprecated.  "
-            "Please use method chaining"
-        ):
+        with testing.expect_deprecated(undefer_needs_chaining):
             sess.query(User).options(defer("addresses", "email_address"))
 
-        with testing.expect_deprecated(
-            r"The \*addl_attrs on orm.undefer is deprecated.  "
-            "Please use method chaining"
-        ):
+        with testing.expect_deprecated(undefer_needs_chaining):
             sess.query(User).options(undefer("addresses", "email_address"))
 
 
@@ -3128,20 +3480,22 @@ class NonPrimaryRelationshipLoaderTest(_fixtures.FixtureTest):
 
         closed_mapper = User.closed_orders.entity
         open_mapper = User.open_orders.entity
-        eq_(
-            [Order(id=1), Order(id=5)],
-            fixture_session()
-            .query(closed_mapper)
-            .with_parent(user, property="closed_orders")
-            .all(),
-        )
-        eq_(
-            [Order(id=3)],
-            fixture_session()
-            .query(open_mapper)
-            .with_parent(user, property="open_orders")
-            .all(),
-        )
+        with testing.expect_deprecated_20(wparent_strings_dep):
+            eq_(
+                [Order(id=1), Order(id=5)],
+                fixture_session()
+                .query(closed_mapper)
+                .with_parent(user, property="closed_orders")
+                .all(),
+            )
+        with testing.expect_deprecated_20(wparent_strings_dep):
+            eq_(
+                [Order(id=3)],
+                fixture_session()
+                .query(open_mapper)
+                .with_parent(user, property="open_orders")
+                .all(),
+            )
 
 
 class ViewonlyFlagWarningTest(fixtures.MappedTest):
@@ -4078,6 +4432,43 @@ class DeclarativeBind(fixtures.TestBase):
 class JoinTest(QueryTest, AssertsCompiledSQL):
     __dialect__ = "default"
 
+    @testing.combinations(
+        "string_relationship",
+        "string_relationship_only",
+    )
+    def test_filter_by_from_join(self, onclause_type):
+        User, Address = self.classes("User", "Address")
+        (address_table,) = self.tables("addresses")
+        (user_table,) = self.tables("users")
+
+        sess = fixture_session()
+        q = sess.query(User)
+
+        with assertions.expect_deprecated_20(join_strings_dep):
+            if onclause_type == "string_relationship":
+                q = q.join(Address, "addresses")
+            elif onclause_type == "string_relationship_only":
+                q = q.join("addresses")
+            else:
+                assert False
+
+        q2 = q.filter_by(email_address="foo")
+
+        self.assert_compile(
+            q2,
+            "SELECT users.id AS users_id, users.name AS users_name "
+            "FROM users JOIN addresses ON users.id = addresses.user_id "
+            "WHERE addresses.email_address = :email_address_1",
+        )
+
+        q2 = q.reset_joinpoint().filter_by(name="user")
+        self.assert_compile(
+            q2,
+            "SELECT users.id AS users_id, users.name AS users_name "
+            "FROM users JOIN addresses ON users.id = addresses.user_id "
+            "WHERE users.name = :name_1",
+        )
+
     def test_implicit_joins_from_aliases(self):
         Item, User, Order = (
             self.classes.Item,
@@ -5266,6 +5657,75 @@ class InheritedJoinTest(
     AssertsCompiledSQL,
 ):
     run_setup_mappers = "once"
+    __dialect__ = "default"
+
+    def test_load_only_alias_subclass(self):
+        Manager = self.classes.Manager
+
+        s = fixture_session()
+        m1 = aliased(Manager, flat=True)
+        q = (
+            s.query(m1)
+            .order_by(m1.person_id)
+            .options(load_only("status", "manager_name"))
+        )
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self.assert_compile(
+                q,
+                "SELECT managers_1.person_id AS managers_1_person_id, "
+                "people_1.person_id AS people_1_person_id, "
+                "people_1.type AS people_1_type, "
+                "managers_1.status AS managers_1_status, "
+                "managers_1.manager_name AS managers_1_manager_name "
+                "FROM people AS people_1 JOIN managers AS "
+                "managers_1 ON people_1.person_id = managers_1.person_id "
+                "ORDER BY managers_1.person_id",
+            )
+
+    def test_load_only_alias_subclass_bound(self):
+        Manager = self.classes.Manager
+
+        s = fixture_session()
+        m1 = aliased(Manager, flat=True)
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            q = (
+                s.query(m1)
+                .order_by(m1.person_id)
+                .options(Load(m1).load_only("status", "manager_name"))
+            )
+        self.assert_compile(
+            q,
+            "SELECT managers_1.person_id AS managers_1_person_id, "
+            "people_1.person_id AS people_1_person_id, "
+            "people_1.type AS people_1_type, "
+            "managers_1.status AS managers_1_status, "
+            "managers_1.manager_name AS managers_1_manager_name "
+            "FROM people AS people_1 JOIN managers AS "
+            "managers_1 ON people_1.person_id = managers_1.person_id "
+            "ORDER BY managers_1.person_id",
+        )
+
+    def test_load_only_of_type_with_polymorphic(self):
+        Company, Person, Manager = self.classes("Company", "Person", "Manager")
+        s = fixture_session()
+
+        wp = with_polymorphic(Person, [Manager], flat=True)
+
+        # needs to be explicit, we don't currently dig onto all the
+        # sub-entities in the wp
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            assert_raises_message(
+                sa.exc.ArgumentError,
+                r'Can\'t find property named "status" on '
+                r"with_polymorphic\(Person, \[Manager\]\) in this Query.",
+                s.query(Company)
+                .options(
+                    joinedload(Company.employees.of_type(wp)).load_only(
+                        "status"
+                    )
+                )
+                ._compile_context,
+            )
 
     def test_join_to_selectable(self):
         people, Company, engineers, Engineer = (
@@ -5957,3 +6417,834 @@ class RequirementsTest(fixtures.MappedTest):
             # TODO: is weakref support detectable without an instance?
             # self.assertRaises(
             #  sa.exc.ArgumentError, mapper, NoWeakrefSupport, t2)
+
+
+class DeferredOptionsTest(AssertsCompiledSQL, _fixtures.FixtureTest):
+    __dialect__ = "default"
+
+    def test_load_only_synonym(self):
+        orders, Order = self.tables.orders, self.classes.Order
+
+        self.mapper_registry.map_imperatively(
+            Order,
+            orders,
+            properties={"desc": synonym("description")},
+        )
+
+        opt = load_only("isopen", "desc")
+
+        sess = fixture_session()
+        q = sess.query(Order).options(opt)
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self.assert_compile(
+                q,
+                "SELECT orders.id AS orders_id, orders.description "
+                "AS orders_description, orders.isopen AS orders_isopen "
+                "FROM orders",
+            )
+
+    def test_deep_options(self):
+        users, items, order_items, Order, Item, User, orders = (
+            self.tables.users,
+            self.tables.items,
+            self.tables.order_items,
+            self.classes.Order,
+            self.classes.Item,
+            self.classes.User,
+            self.tables.orders,
+        )
+
+        self.mapper_registry.map_imperatively(
+            Item,
+            items,
+            properties=dict(description=deferred(items.c.description)),
+        )
+        self.mapper_registry.map_imperatively(
+            Order,
+            orders,
+            properties=dict(items=relationship(Item, secondary=order_items)),
+        )
+        self.mapper_registry.map_imperatively(
+            User,
+            users,
+            properties=dict(orders=relationship(Order, order_by=orders.c.id)),
+        )
+
+        sess = fixture_session()
+        q = sess.query(User).order_by(User.id)
+        result = q.all()
+        item = result[0].orders[1].items[1]
+
+        def go():
+            eq_(item.description, "item 4")
+
+        self.sql_count_(1, go)
+        eq_(item.description, "item 4")
+
+        sess.expunge_all()
+        with assertions.expect_deprecated(undefer_needs_chaining):
+            result = q.options(
+                undefer(User.orders, Order.items, Item.description)
+            ).all()
+        item = result[0].orders[1].items[1]
+
+        def go():
+            eq_(item.description, "item 4")
+
+        self.sql_count_(0, go)
+        eq_(item.description, "item 4")
+
+
+class OptionsTest(PathTest, OptionsQueryTest):
+    def _option_fixture(self, *arg):
+        return strategy_options._UnboundLoad._from_keys(
+            strategy_options._UnboundLoad.joinedload, arg, True, {}
+        )
+
+    def test_chained(self):
+        User = self.classes.User
+        Order = self.classes.Order
+        sess = fixture_session()
+        q = sess.query(User)
+        opt = self._option_fixture(User.orders).joinedload("items")
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_path_result(
+                opt, q, [(User, "orders"), (User, "orders", Order, "items")]
+            )
+
+    def test_chained_plus_dotted(self):
+        User = self.classes.User
+        Order = self.classes.Order
+        Item = self.classes.Item
+        sess = fixture_session()
+        q = sess.query(User)
+        opt = self._option_fixture("orders.items").joinedload("keywords")
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_path_result(
+                opt,
+                q,
+                [
+                    (User, "orders"),
+                    (User, "orders", Order, "items"),
+                    (User, "orders", Order, "items", Item, "keywords"),
+                ],
+            )
+
+    def test_with_current_matching_string(self):
+        Item, User, Order = (
+            self.classes.Item,
+            self.classes.User,
+            self.classes.Order,
+        )
+
+        sess = fixture_session()
+        q = sess.query(Item)._with_current_path(
+            self._make_path_registry([User, "orders", Order, "items"])
+        )
+
+        opt = self._option_fixture("orders.items.keywords")
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_path_result(opt, q, [(Item, "keywords")])
+
+    def test_with_current_nonmatching_string(self):
+        Item, User, Order = (
+            self.classes.Item,
+            self.classes.User,
+            self.classes.Order,
+        )
+
+        sess = fixture_session()
+        q = sess.query(Item)._with_current_path(
+            self._make_path_registry([User, "orders", Order, "items"])
+        )
+
+        opt = self._option_fixture("keywords")
+        self._assert_path_result(opt, q, [])
+
+        opt = self._option_fixture("items.keywords")
+        self._assert_path_result(opt, q, [])
+
+    def test_path_multilevel_string(self):
+        Item, User, Order = (
+            self.classes.Item,
+            self.classes.User,
+            self.classes.Order,
+        )
+
+        sess = fixture_session()
+        q = sess.query(User)
+
+        opt = self._option_fixture("orders.items.keywords")
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_path_result(
+                opt,
+                q,
+                [
+                    (User, "orders"),
+                    (User, "orders", Order, "items"),
+                    (User, "orders", Order, "items", Item, "keywords"),
+                ],
+            )
+
+    def test_chained_plus_multi(self):
+        User = self.classes.User
+        Order = self.classes.Order
+        Item = self.classes.Item
+        sess = fixture_session()
+        q = sess.query(User)
+        opt = self._option_fixture(User.orders, Order.items).joinedload(
+            "keywords"
+        )
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_path_result(
+                opt,
+                q,
+                [
+                    (User, "orders"),
+                    (User, "orders", Order, "items"),
+                    (User, "orders", Order, "items", Item, "keywords"),
+                ],
+            )
+
+    def test_multi_entity_opt_on_string(self):
+        Item = self.classes.Item
+        Order = self.classes.Order
+        opt = self._option_fixture("items")
+        sess = fixture_session()
+        q = sess.query(Item, Order)
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_path_result(opt, q, [])
+
+    def test_get_path_one_level_string(self):
+        User = self.classes.User
+
+        sess = fixture_session()
+        q = sess.query(User)
+
+        opt = self._option_fixture("addresses")
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_path_result(opt, q, [(User, "addresses")])
+
+    def test_get_path_one_level_with_unrelated(self):
+        Order = self.classes.Order
+
+        sess = fixture_session()
+        q = sess.query(Order)
+        opt = self._option_fixture("addresses")
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_path_result(opt, q, [])
+
+
+class SubOptionsTest(PathTest, OptionsQueryTest):
+    run_create_tables = False
+    run_inserts = None
+    run_deletes = None
+
+    def _assert_opts(self, q, sub_opt, non_sub_opts):
+        attr_a = {}
+
+        for val in sub_opt._to_bind:
+            val._bind_loader(
+                [
+                    ent.entity_zero
+                    for ent in q._compile_state()._lead_mapper_entities
+                ],
+                q._compile_options._current_path,
+                attr_a,
+                False,
+            )
+
+        attr_b = {}
+
+        for opt in non_sub_opts:
+            for val in opt._to_bind:
+                val._bind_loader(
+                    [
+                        ent.entity_zero
+                        for ent in q._compile_state()._lead_mapper_entities
+                    ],
+                    q._compile_options._current_path,
+                    attr_b,
+                    False,
+                )
+
+        for k, l in attr_b.items():
+            if not l.strategy:
+                del attr_b[k]
+
+        def strat_as_tuple(strat):
+            return (
+                strat.strategy,
+                strat.local_opts,
+                strat.propagate_to_loaders,
+                strat._of_type,
+                strat.is_class_strategy,
+                strat.is_opts_only,
+            )
+
+        eq_(
+            {path: strat_as_tuple(load) for path, load in attr_a.items()},
+            {path: strat_as_tuple(load) for path, load in attr_b.items()},
+        )
+
+    def test_invalid_two(self):
+        User, Address, Order, Item, SubItem = self.classes(
+            "User", "Address", "Order", "Item", "SubItem"
+        )
+
+        # these options are "invalid", in that User.orders -> Item.keywords
+        # is not a path.  However, the "normal" option is not generating
+        # an error for now, which is bad, but we're testing here only that
+        # it works the same way, so there you go.   If and when we make this
+        # case raise, then both cases should raise in the same way.
+        sub_opt = joinedload("orders").options(
+            joinedload("keywords"), joinedload("items")
+        )
+        non_sub_opts = [
+            joinedload(User.orders).joinedload(Item.keywords),
+            defaultload(User.orders).joinedload(Order.items),
+        ]
+        sess = fixture_session()
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_opts(sess.query(User), sub_opt, non_sub_opts)
+
+    def test_four_strings(self):
+        User, Address, Order, Item, SubItem, Keyword = self.classes(
+            "User", "Address", "Order", "Item", "SubItem", "Keyword"
+        )
+        sub_opt = joinedload("orders").options(
+            defer("description"),
+            joinedload("items").options(
+                joinedload("keywords").options(defer("name")),
+                defer("description"),
+            ),
+        )
+        non_sub_opts = [
+            joinedload(User.orders),
+            defaultload(User.orders).defer(Order.description),
+            defaultload(User.orders).joinedload(Order.items),
+            defaultload(User.orders)
+            .defaultload(Order.items)
+            .joinedload(Item.keywords),
+            defaultload(User.orders)
+            .defaultload(Order.items)
+            .defer(Item.description),
+            defaultload(User.orders)
+            .defaultload(Order.items)
+            .defaultload(Item.keywords)
+            .defer(Keyword.name),
+        ]
+        sess = fixture_session()
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_opts(sess.query(User), sub_opt, non_sub_opts)
+
+    def test_five_strings(self):
+        User, Address, Order, Item, SubItem, Keyword = self.classes(
+            "User", "Address", "Order", "Item", "SubItem", "Keyword"
+        )
+        sub_opt = joinedload("orders").options(load_only("description"))
+        non_sub_opts = [
+            joinedload(User.orders),
+            defaultload(User.orders).load_only(Order.description),
+        ]
+        sess = fixture_session()
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_opts(sess.query(User), sub_opt, non_sub_opts)
+
+
+class OptionsNoPropTest(_fixtures.FixtureTest):
+    """test the error messages emitted when using property
+    options in conjunction with column-only entities, or
+    for not existing options
+
+    """
+
+    run_create_tables = False
+    run_inserts = None
+    run_deletes = None
+
+    def test_option_with_column_basestring(self):
+        Item = self.classes.Item
+
+        message = (
+            "Query has only expression-based entities - can't "
+            'find property named "keywords".'
+        )
+        self._assert_eager_with_just_column_exception(
+            Item.id, "keywords", message
+        )
+
+    def test_option_against_nonexistent_basestring(self):
+        Item = self.classes.Item
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_eager_with_entity_exception(
+                [Item],
+                (joinedload("foo"),),
+                'Can\'t find property named "foo" on mapped class '
+                "Item->items in this Query.",
+            )
+
+    def test_option_against_nonexistent_twolevel_basestring(self):
+        Item = self.classes.Item
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_eager_with_entity_exception(
+                [Item],
+                (joinedload("keywords.foo"),),
+                'Can\'t find property named "foo" on mapped class '
+                "Keyword->keywords in this Query.",
+            )
+
+    def test_option_against_nonexistent_twolevel_chained(self):
+        Item = self.classes.Item
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_eager_with_entity_exception(
+                [Item],
+                (joinedload("keywords").joinedload("foo"),),
+                'Can\'t find property named "foo" on mapped class '
+                "Keyword->keywords in this Query.",
+            )
+
+    @testing.fails_if(
+        lambda: True,
+        "PropertyOption doesn't yet check for relation/column on end result",
+    )
+    def test_option_against_non_relation_basestring(self):
+        Item = self.classes.Item
+        Keyword = self.classes.Keyword
+        self._assert_eager_with_entity_exception(
+            [Keyword, Item],
+            (joinedload("keywords"),),
+            r"Attribute 'keywords' of entity 'Mapper\|Keyword\|keywords' "
+            "does not refer to a mapped entity",
+        )
+
+    @testing.fails_if(
+        lambda: True,
+        "PropertyOption doesn't yet check for relation/column on end result",
+    )
+    def test_option_against_multi_non_relation_basestring(self):
+        Item = self.classes.Item
+        Keyword = self.classes.Keyword
+        self._assert_eager_with_entity_exception(
+            [Keyword, Item],
+            (joinedload("keywords"),),
+            r"Attribute 'keywords' of entity 'Mapper\|Keyword\|keywords' "
+            "does not refer to a mapped entity",
+        )
+
+    def test_option_against_wrong_entity_type_basestring(self):
+        Item = self.classes.Item
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_loader_strategy_exception(
+                [Item],
+                (joinedload("id").joinedload("keywords"),),
+                'Can\'t apply "joined loader" strategy to property "Item.id", '
+                'which is a "column property"; this loader strategy is '
+                'intended to be used with a "relationship property".',
+            )
+
+    def test_col_option_against_relationship_basestring(self):
+        Item = self.classes.Item
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_loader_strategy_exception(
+                [Item],
+                (load_only("keywords"),),
+                'Can\'t apply "column loader" strategy to property '
+                '"Item.keywords", which is a "relationship property"; this '
+                "loader strategy is intended to be used with a "
+                '"column property".',
+            )
+
+    def test_option_against_multi_non_relation_twolevel_basestring(self):
+        Item = self.classes.Item
+        Keyword = self.classes.Keyword
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_loader_strategy_exception(
+                [Keyword, Item],
+                (joinedload("id").joinedload("keywords"),),
+                'Can\'t apply "joined loader" strategy to property '
+                '"Keyword.id", '
+                'which is a "column property"; this loader strategy is '
+                "intended "
+                'to be used with a "relationship property".',
+            )
+
+    def test_option_against_multi_nonexistent_basestring(self):
+        Item = self.classes.Item
+        Keyword = self.classes.Keyword
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_eager_with_entity_exception(
+                [Keyword, Item],
+                (joinedload("description"),),
+                'Can\'t find property named "description" on mapped class '
+                "Keyword->keywords in this Query.",
+            )
+
+    def test_option_against_multi_no_entities_basestring(self):
+        Item = self.classes.Item
+        Keyword = self.classes.Keyword
+        self._assert_eager_with_entity_exception(
+            [Keyword.id, Item.id],
+            (joinedload("keywords"),),
+            r"Query has only expression-based entities - can't find property "
+            'named "keywords".',
+        )
+
+    def test_option_with_mapper_then_column_basestring(self):
+        Item = self.classes.Item
+
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_option([Item, Item.id], "keywords")
+
+    def test_option_with_mapper_basestring(self):
+        Item = self.classes.Item
+
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_option([Item], "keywords")
+
+    def test_option_with_column_then_mapper_basestring(self):
+        Item = self.classes.Item
+
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self._assert_option([Item.id, Item], "keywords")
+
+    @classmethod
+    def setup_mappers(cls):
+        users, User, addresses, Address, orders, Order = (
+            cls.tables.users,
+            cls.classes.User,
+            cls.tables.addresses,
+            cls.classes.Address,
+            cls.tables.orders,
+            cls.classes.Order,
+        )
+        cls.mapper_registry.map_imperatively(
+            User,
+            users,
+            properties={
+                "addresses": relationship(Address),
+                "orders": relationship(Order),
+            },
+        )
+        cls.mapper_registry.map_imperatively(Address, addresses)
+        cls.mapper_registry.map_imperatively(Order, orders)
+        keywords, items, item_keywords, Keyword, Item = (
+            cls.tables.keywords,
+            cls.tables.items,
+            cls.tables.item_keywords,
+            cls.classes.Keyword,
+            cls.classes.Item,
+        )
+        cls.mapper_registry.map_imperatively(
+            Keyword,
+            keywords,
+            properties={
+                "keywords": column_property(keywords.c.name + "some keyword")
+            },
+        )
+        cls.mapper_registry.map_imperatively(
+            Item,
+            items,
+            properties=dict(
+                keywords=relationship(Keyword, secondary=item_keywords)
+            ),
+        )
+
+        class OrderWProp(cls.classes.Order):
+            @property
+            def some_attr(self):
+                return "hi"
+
+        cls.mapper_registry.map_imperatively(
+            OrderWProp, None, inherits=cls.classes.Order
+        )
+
+    def _assert_option(self, entity_list, option):
+        Item = self.classes.Item
+
+        context = (
+            fixture_session()
+            .query(*entity_list)
+            .options(joinedload(option))
+            ._compile_state()
+        )
+        key = ("loader", (inspect(Item), inspect(Item).attrs.keywords))
+        assert key in context.attributes
+
+    def _assert_loader_strategy_exception(self, entity_list, options, message):
+        assert_raises_message(
+            orm_exc.LoaderStrategyException,
+            message,
+            fixture_session()
+            .query(*entity_list)
+            .options(*options)
+            ._compile_state,
+        )
+
+    def _assert_eager_with_entity_exception(
+        self, entity_list, options, message
+    ):
+        assert_raises_message(
+            sa.exc.ArgumentError,
+            message,
+            fixture_session()
+            .query(*entity_list)
+            .options(*options)
+            ._compile_state,
+        )
+
+    def _assert_eager_with_just_column_exception(
+        self, column, eager_option, message
+    ):
+        assert_raises_message(
+            sa.exc.ArgumentError,
+            message,
+            fixture_session()
+            .query(column)
+            .options(joinedload(eager_option))
+            ._compile_state,
+        )
+
+
+class OptionsNoPropTestInh(_Polymorphic):
+    def test_missing_str_attr_of_type_subclass(self):
+        s = fixture_session()
+
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            assert_raises_message(
+                sa.exc.ArgumentError,
+                r'Can\'t find property named "manager_name" on '
+                r"mapped class Engineer->engineers in this Query.$",
+                s.query(Company)
+                .options(
+                    joinedload(Company.employees.of_type(Engineer)).load_only(
+                        "manager_name"
+                    )
+                )
+                ._compile_state,
+            )
+
+
+class CacheKeyTest(CacheKeyFixture, _fixtures.FixtureTest):
+    """In these tests we've moved / adapted all the tests from
+    test_cache_key that make use of string options or string join().  Because
+    we are ensuring cache keys are distinct we still keep a lot of the
+    non-deprecated cases in the lists that we are testing.
+
+    """
+
+    run_setup_mappers = "once"
+    run_inserts = None
+    run_deletes = None
+
+    @classmethod
+    def setup_mappers(cls):
+        cls._setup_stock_mapping()
+
+    def _stmt_20(self, *elements):
+        return tuple(
+            elem._statement_20() if isinstance(elem, sa.orm.Query) else elem
+            for elem in elements
+        )
+
+    def _deprecated_opt(self, fn):
+        with assertions.expect_deprecated_20(
+            opt_strings_dep, raise_on_any_unexpected=True
+        ):
+            return fn()
+
+    def _deprecated_join(self, fn):
+        with assertions.expect_deprecated_20(
+            join_strings_dep, raise_on_any_unexpected=True
+        ):
+            return fn()
+
+    def _deprecated_join_w_aliased(self, fn):
+        with assertions.expect_deprecated_20(
+            join_strings_dep, join_aliased_dep, raise_on_any_unexpected=True
+        ):
+            return fn()
+
+    def test_bound_options(self):
+        User, Address, Keyword, Order, Item = self.classes(
+            "User", "Address", "Keyword", "Order", "Item"
+        )
+
+        self._run_cache_key_fixture(
+            lambda: (
+                Load(User).joinedload(User.addresses),
+                Load(User).joinedload(
+                    User.addresses.of_type(aliased(Address))
+                ),
+                Load(User).joinedload(User.orders),
+                self._deprecated_opt(
+                    lambda: Load(User).subqueryload("addresses")
+                ),
+                self._deprecated_opt(lambda: Load(Address).defer("id")),
+                Load(Address).defer("*"),
+                self._deprecated_opt(
+                    lambda: Load(aliased(Address)).defer("id")
+                ),
+                Load(User).joinedload(User.addresses).defer(Address.id),
+                Load(User).joinedload(User.orders).joinedload(Order.items),
+                Load(User).joinedload(User.orders).subqueryload(Order.items),
+                Load(User).subqueryload(User.orders).subqueryload(Order.items),
+                Load(Address).raiseload("*"),
+                self._deprecated_opt(lambda: Load(Address).raiseload("user")),
+            ),
+            compare_values=True,
+        )
+
+    def test_bound_options_equiv_on_strname(self):
+        """Bound loader options resolve on string name so test that the cache
+        key for the string version matches the resolved version.
+
+        """
+        User, Address, Keyword, Order, Item = self.classes(
+            "User", "Address", "Keyword", "Order", "Item"
+        )
+
+        for left, right in [
+            (
+                Load(User).defer(User.id),
+                self._deprecated_opt(lambda: Load(User).defer("id")),
+            ),
+            (
+                Load(User).joinedload(User.addresses),
+                self._deprecated_opt(
+                    lambda: Load(User).joinedload("addresses")
+                ),
+            ),
+            (
+                Load(User).joinedload(User.orders).joinedload(Order.items),
+                self._deprecated_opt(
+                    lambda: Load(User).joinedload("orders").joinedload("items")
+                ),
+            ),
+        ]:
+            eq_(left._generate_cache_key(), right._generate_cache_key())
+
+    def test_orm_query_w_orm_joins(self):
+
+        User, Address, Keyword, Order, Item = self.classes(
+            "User", "Address", "Keyword", "Order", "Item"
+        )
+
+        a1 = aliased(Address)
+
+        self._run_cache_key_fixture(
+            lambda: self._stmt_20(
+                fixture_session().query(User).join(User.addresses),
+                fixture_session().query(User).join(User.orders),
+                fixture_session()
+                .query(User)
+                .join(User.addresses)
+                .join(User.orders),
+                self._deprecated_join_w_aliased(
+                    lambda: fixture_session()
+                    .query(User)
+                    .join("addresses")
+                    .join("dingalings", from_joinpoint=True)
+                ),
+                self._deprecated_join(
+                    lambda: fixture_session().query(User).join("addresses")
+                ),
+                self._deprecated_join(
+                    lambda: fixture_session().query(User).join("orders")
+                ),
+                self._deprecated_join(
+                    lambda: fixture_session()
+                    .query(User)
+                    .join("addresses")
+                    .join("orders")
+                ),
+                fixture_session().query(User).join(Address, User.addresses),
+                self._deprecated_join(
+                    lambda: fixture_session().query(User).join(a1, "addresses")
+                ),
+                self._deprecated_join_w_aliased(
+                    lambda: fixture_session()
+                    .query(User)
+                    .join(a1, "addresses", aliased=True)
+                ),
+                fixture_session().query(User).join(User.addresses.of_type(a1)),
+            ),
+            compare_values=True,
+        )
+
+    def test_unbound_options(self):
+        User, Address, Keyword, Order, Item = self.classes(
+            "User", "Address", "Keyword", "Order", "Item"
+        )
+
+        # unbound options dont emit a deprecation warning during cache
+        # key generation
+        self._run_cache_key_fixture(
+            lambda: (
+                joinedload(User.addresses),
+                joinedload(User.addresses.of_type(aliased(Address))),
+                joinedload("addresses"),
+                joinedload(User.orders),
+                joinedload(User.orders.and_(Order.id != 5)),
+                joinedload(User.orders).selectinload("items"),
+                joinedload(User.orders).selectinload(Order.items),
+                defer(User.id),
+                defer("id"),
+                defer("*"),
+                defer(Address.id),
+                joinedload(User.addresses).defer(Address.id),
+                joinedload(User.addresses).defer("id"),
+                subqueryload(User.orders)
+                .subqueryload(Order.items)
+                .defer(Item.description),
+                defaultload(User.orders).defaultload(Order.items),
+                defaultload(User.orders),
+            ),
+            compare_values=True,
+        )
+
+    def test_unbound_sub_options(self):
+        """test #6869"""
+
+        User, Address, Keyword, Order, Item = self.classes(
+            "User", "Address", "Keyword", "Order", "Item"
+        )
+
+        self._run_cache_key_fixture(
+            lambda: (
+                joinedload(User.addresses).options(
+                    joinedload(Address.dingaling)
+                ),
+                joinedload(User.addresses).options(
+                    joinedload(Address.dingaling).options(load_only("name"))
+                ),
+                joinedload(User.orders).options(
+                    joinedload(Order.items).options(joinedload(Item.keywords))
+                ),
+            ),
+            compare_values=True,
+        )
+
+
+class AliasedClassRelationshipTest(
+    PartitionByFixture, testing.AssertsCompiledSQL
+):
+    __requires__ = ("window_functions",)
+    __dialect__ = "default"
+
+    def test_selectinload_w_joinedload_after(self):
+        """test has been enhanced to also test #7224"""
+
+        A, B, C = self.classes("A", "B", "C")
+
+        s = Session(testing.db)
+
+        opt = selectinload(A.partitioned_bs).joinedload("cs")
+
+        def go():
+            for a1 in s.query(A).options(opt):
+                for b in a1.partitioned_bs:
+                    eq_(len(b.cs), 2)
+
+        with assertions.expect_deprecated_20(opt_strings_dep):
+            self.assert_sql_count(testing.db, go, 2)
index cb578ee2dad53440eddf5821ca4a826c011a57ab..c235edadfae5fe52c646c8e07fed22a5f40290d2 100644 (file)
@@ -754,7 +754,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL):
         def go():
             eq_(
                 self.static.item_keyword_result[0:2],
-                q.join("keywords").filter(Keyword.name == "red").all(),
+                q.join(Item.keywords).filter(Keyword.name == "red").all(),
             )
 
         self.assert_sql_count(testing.db, go, 1)
@@ -763,7 +763,9 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL):
             ka = aliased(Keyword)
             eq_(
                 self.static.item_keyword_result[0:2],
-                (q.join(ka, "keywords").filter(ka.name == "red")).all(),
+                (
+                    q.join(Item.keywords.of_type(ka)).filter(ka.name == "red")
+                ).all(),
             )
 
         self.assert_sql_count(testing.db, go, 1)
@@ -1401,7 +1403,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL):
 
         if not testing.against("mssql"):
             result = (
-                q.join("orders")
+                q.join(User.orders)
                 .order_by(Order.user_id.desc())
                 .limit(2)
                 .offset(1)
@@ -1423,7 +1425,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL):
             )
 
         result = (
-            q.join("addresses")
+            q.join(User.addresses)
             .order_by(Address.email_address.desc())
             .limit(1)
             .offset(0)
@@ -4040,7 +4042,7 @@ class AddEntityTest(_fixtures.FixtureTest):
         def go():
             ret = (
                 sess.query(User, oalias)
-                .join(oalias, "orders")
+                .join(User.orders.of_type(oalias))
                 .order_by(User.id, oalias.id)
                 .all()
             )
@@ -4099,7 +4101,7 @@ class AddEntityTest(_fixtures.FixtureTest):
             ret = (
                 sess.query(User, oalias)
                 .options(joinedload(User.addresses))
-                .join(oalias, "orders")
+                .join(User.orders.of_type(oalias))
                 .order_by(User.id, oalias.id)
                 .all()
             )
@@ -4113,7 +4115,7 @@ class AddEntityTest(_fixtures.FixtureTest):
             ret = (
                 sess.query(User, oalias)
                 .options(joinedload(User.addresses), joinedload(oalias.items))
-                .join(oalias, "orders")
+                .join(User.orders.of_type(oalias))
                 .order_by(User.id, oalias.id)
                 .all()
             )
@@ -5972,7 +5974,9 @@ class EntityViaMultiplePathTestTwo(fixtures.DeclarativeMappedTest):
             s.query(LDA)
             .join(LDA.ld)
             .options(contains_eager(LDA.ld))
-            .join("a", (l_ac, "ld"), (u_ac, "user"))
+            .join(LDA.a)
+            .join(A.ld.of_type(l_ac))
+            .join(l_ac.user.of_type(u_ac))
             .options(
                 contains_eager(LDA.a)
                 .contains_eager(A.ld, alias=l_ac)
index 92a7ea42d37e49110d418b1dd2375854942e1f75..d9204db96c84c84f8290cfaa3c8c512ea867789b 100644 (file)
@@ -1157,7 +1157,7 @@ class ExpireTest(_fixtures.FixtureTest):
         assert "addresses" not in u.__dict__
         (
             sess.query(User)
-            .options(sa.orm.joinedload("addresses"))
+            .options(sa.orm.joinedload(User.addresses))
             .filter_by(id=8)
             .all()
         )
@@ -1206,7 +1206,7 @@ class ExpireTest(_fixtures.FixtureTest):
         sess.expunge_all()
 
         # same tests, using deferred at the options level
-        o = sess.query(Order).options(sa.orm.defer("description")).get(3)
+        o = sess.query(Order).options(sa.orm.defer(Order.description)).get(3)
 
         assert "description" not in o.__dict__
 
@@ -1983,7 +1983,7 @@ class RefreshTest(_fixtures.FixtureTest):
                 )
             },
         )
-        q = s.query(User).options(sa.orm.lazyload("addresses"))
+        q = s.query(User).options(sa.orm.lazyload(User.addresses))
         u = q.filter(users.c.id == 8).first()
 
         def go():
@@ -2049,7 +2049,9 @@ class RefreshTest(_fixtures.FixtureTest):
         q = (
             s.query(User)
             .filter_by(name="fred")
-            .options(sa.orm.lazyload("addresses").joinedload("dingalings"))
+            .options(
+                sa.orm.lazyload(User.addresses).joinedload(Address.dingalings)
+            )
         )
 
         u1 = q.one()
index d6579cebd1d5778f6f5f4075a75b36724771133c..3351967fc1136aebc1b10a21fe697d9b5c8e2ecd 100644 (file)
@@ -1098,7 +1098,7 @@ class InstancesTest(QueryTest, AssertsCompiledSQL):
 
         def go():
             result = (
-                q.options(contains_eager("addresses"))
+                q.options(contains_eager(User.addresses))
                 .from_statement(query)
                 .all()
             )
@@ -1129,7 +1129,7 @@ class InstancesTest(QueryTest, AssertsCompiledSQL):
             ulist = query.alias("ulist")
             ulist_alias = aliased(User, alias=ulist)
             result = (
-                q.options(contains_eager("addresses", alias=ulist))
+                q.options(contains_eager(User.addresses, alias=ulist))
                 .select_entity_from(ulist_alias)
                 .all()
             )
@@ -1160,7 +1160,7 @@ class InstancesTest(QueryTest, AssertsCompiledSQL):
             result = (
                 sess.query(User)
                 .select_entity_from(query.subquery())
-                .options(contains_eager("addresses"))
+                .options(contains_eager(User.addresses))
                 .all()
             )
             assert self.static.user_address_result == result
@@ -1194,7 +1194,7 @@ class InstancesTest(QueryTest, AssertsCompiledSQL):
             result = (
                 sess.query(User)
                 .select_entity_from(query.subquery())
-                .options(contains_eager("addresses", alias=adalias))
+                .options(contains_eager(User.addresses, alias=adalias))
                 .all()
             )
             assert self.static.user_address_result == result
@@ -1273,7 +1273,7 @@ class InstancesTest(QueryTest, AssertsCompiledSQL):
 
         def go():
             result = (
-                q.options(contains_eager("addresses"))
+                q.options(contains_eager(User.addresses))
                 .from_statement(selectquery)
                 .all()
             )
@@ -1302,7 +1302,7 @@ class InstancesTest(QueryTest, AssertsCompiledSQL):
         def go():
             result = (
                 sess.execute(
-                    q.options(contains_eager("addresses")).from_statement(
+                    q.options(contains_eager(User.addresses)).from_statement(
                         selectquery
                     )
                 )
@@ -1325,7 +1325,7 @@ class InstancesTest(QueryTest, AssertsCompiledSQL):
 
         def go():
             result = (
-                q.options(contains_eager("addresses", alias=adalias))
+                q.options(contains_eager(User.addresses.of_type(adalias)))
                 .outerjoin(adalias, User.addresses)
                 .order_by(User.id, adalias.id)
             )
@@ -1341,6 +1341,7 @@ class InstancesTest(QueryTest, AssertsCompiledSQL):
             self.tables.order_items,
             self.classes.User,
         )
+        Order = self.classes.Order
 
         sess = fixture_session()
         q = sess.query(User)
@@ -1357,16 +1358,12 @@ class InstancesTest(QueryTest, AssertsCompiledSQL):
 
         # test using Alias with more than one level deep
 
-        # new way:
-        # from sqlalchemy.orm.strategy_options import Load
-        # opt = Load(User).contains_eager('orders', alias=oalias).
-        #     contains_eager('items', alias=ialias)
-
         def go():
             result = list(
                 q.options(
-                    contains_eager("orders", alias=oalias),
-                    contains_eager("orders.items", alias=ialias),
+                    contains_eager(User.orders, alias=oalias).contains_eager(
+                        Order.items, alias=ialias
+                    ),
                 ).from_statement(query)
             )
             assert self.static.user_order_result == result
@@ -1869,7 +1866,7 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
         adalias = aliased(Address)
         eq_(
             sess.query(User, func.count(adalias.email_address))
-            .outerjoin(adalias, "addresses")
+            .outerjoin(User.addresses.of_type(adalias))
             .group_by(User)
             .order_by(User.id)
             .all(),
@@ -2110,7 +2107,7 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
             results = (
                 sess.query(User)
                 .limit(1)
-                .options(joinedload("addresses"))
+                .options(joinedload(User.addresses))
                 .add_columns(User.name)
                 .all()
             )
@@ -2321,21 +2318,21 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
             q = (
                 sess.query(User)
                 .add_entity(address_entity)
-                .outerjoin(address_entity, "addresses")
+                .outerjoin(address_entity, User.addresses)
                 .order_by(User.id, address_entity.id)
             )
             eq_(q.all(), expected)
             sess.expunge_all()
 
             q = sess.query(User).add_entity(address_entity)
-            q = q.join(address_entity, "addresses")
+            q = q.join(address_entity, User.addresses)
             q = q.filter_by(email_address="ed@bettyboop.com")
             eq_(q.all(), [(user8, address3)])
             sess.expunge_all()
 
             q = (
                 sess.query(User, address_entity)
-                .join(address_entity, "addresses")
+                .join(address_entity, User.addresses)
                 .filter_by(email_address="ed@bettyboop.com")
             )
             eq_(q.all(), [(user8, address3)])
@@ -2343,8 +2340,8 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
 
             q = (
                 sess.query(User, address_entity)
-                .join(address_entity, "addresses")
-                .options(joinedload("addresses"))
+                .join(address_entity, User.addresses)
+                .options(joinedload(User.addresses))
                 .filter_by(email_address="ed@bettyboop.com")
             )
             eq_(list(util.OrderedSet(q.all())), [(user8, address3)])
@@ -2458,7 +2455,7 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
         q = (
             q.group_by(users)
             .order_by(User.id)
-            .outerjoin("addresses")
+            .outerjoin(User.addresses)
             .add_columns(func.count(Address.id).label("count"))
         )
         eq_(q.all(), expected)
@@ -2469,7 +2466,7 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
         q = (
             q.group_by(users)
             .order_by(User.id)
-            .outerjoin(adalias, "addresses")
+            .outerjoin(User.addresses.of_type(adalias))
             .add_columns(func.count(adalias.id).label("count"))
         )
         eq_(q.all(), expected)
@@ -2576,7 +2573,7 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
                 .add_columns(
                     func.count(addresses.c.id), ("Name:" + users.c.name)
                 )
-                .outerjoin("addresses")
+                .outerjoin(User.addresses)
                 .group_by(users)
                 .order_by(users.c.id)
             )
@@ -2604,21 +2601,21 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
         for crit, j, exp in [
             (
                 User.id + Address.id,
-                User.addresses,
+                (User.addresses,),
                 "SELECT users.id + addresses.id AS anon_1 "
                 "FROM users JOIN addresses ON users.id = "
                 "addresses.user_id",
             ),
             (
                 User.id + Address.id,
-                Address.user,
+                (Address.user,),
                 "SELECT users.id + addresses.id AS anon_1 "
                 "FROM addresses JOIN users ON users.id = "
                 "addresses.user_id",
             ),
             (
                 Address.id + User.id,
-                User.addresses,
+                (User.addresses,),
                 "SELECT addresses.id + users.id AS anon_1 "
                 "FROM users JOIN addresses ON users.id = "
                 "addresses.user_id",
@@ -2634,13 +2631,13 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
             q = s.query(crit)
             mzero = q._compile_state()._entity_zero()
             is_(mzero, q._compile_state()._entities[0].entity_zero)
-            q = q.join(j)
+            q = q.join(*j)
             self.assert_compile(q, exp)
 
         for crit, j, exp in [
             (
                 ua.id + Address.id,
-                ua.addresses,
+                (ua.addresses,),
                 "SELECT users_1.id + addresses.id AS anon_1 "
                 "FROM users AS users_1 JOIN addresses "
                 "ON users_1.id = addresses.user_id",
@@ -2664,7 +2661,7 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
             q = s.query(crit)
             mzero = q._compile_state()._entity_zero()
             is_(mzero, q._compile_state()._entities[0].entity_zero)
-            q = q.join(j)
+            q = q.join(*j)
             self.assert_compile(q, exp)
 
     def test_aliased_adapt_on_names(self):
@@ -2764,7 +2761,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         eq_(
             sess.query(User)
             .select_entity_from(sel)
-            .options(joinedload("addresses"))
+            .options(joinedload(User.addresses))
             .first(),
             User(name="jack", addresses=[Address(id=1)]),
         )
@@ -2980,7 +2977,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         sess = fixture_session()
 
         self.assert_compile(
-            sess.query(User).select_from(Address).join("user"),
+            sess.query(User).select_from(Address).join(Address.user),
             "SELECT users.id AS users_id, users.name AS users_name "
             "FROM addresses JOIN users ON users.id = addresses.user_id",
         )
@@ -3050,7 +3047,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         eq_(
             sess.query(User)
             .select_entity_from(sel.subquery())
-            .join("addresses")
+            .join(User.addresses)
             .add_entity(Address)
             .order_by(User.id)
             .order_by(Address.id)
@@ -3079,7 +3076,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         eq_(
             sess.query(User)
             .select_entity_from(sel.subquery())
-            .join(adalias, "addresses")
+            .join(adalias, User.addresses)
             .add_entity(adalias)
             .order_by(User.id)
             .order_by(adalias.id)
@@ -3161,7 +3158,9 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         eq_(
             sess.query(User)
             .select_entity_from(sel.subquery())
-            .join(User.orders, Order.items, Item.keywords)
+            .join(User.orders)
+            .join(Order.items)
+            .join(Item.keywords)
             .filter(Keyword.name.in_(["red", "big", "round"]))
             .all(),
             [User(name="jack", id=7)],
@@ -3226,11 +3225,13 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
                 sess.query(User)
                 .select_entity_from(sel.subquery())
                 .options(
-                    joinedload("orders")
-                    .joinedload("items")
-                    .joinedload("keywords")
+                    joinedload(User.orders)
+                    .joinedload(Order.items)
+                    .joinedload(Item.keywords)
                 )
-                .join(User.orders, Order.items, Item.keywords)
+                .join(User.orders)
+                .join(Order.items)
+                .join(Item.keywords)
                 .filter(Keyword.name.in_(["red", "big", "round"]))
                 .all(),
                 [
@@ -3337,7 +3338,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         def go():
             eq_(
                 sess.query(User)
-                .options(joinedload("addresses"))
+                .options(joinedload(User.addresses))
                 .select_entity_from(sel.subquery())
                 .order_by(User.id)
                 .all(),
@@ -3360,7 +3361,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         def go():
             eq_(
                 sess.query(User)
-                .options(joinedload("addresses"))
+                .options(joinedload(User.addresses))
                 .select_entity_from(sel.subquery())
                 .filter(User.id == 8)
                 .order_by(User.id)
@@ -3383,7 +3384,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         def go():
             eq_(
                 sess.query(User)
-                .options(joinedload("addresses"))
+                .options(joinedload(User.addresses))
                 .select_entity_from(sel.subquery())
                 .order_by(User.id)[1],
                 User(
@@ -3398,79 +3399,6 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
 class CustomJoinTest(QueryTest):
     run_setup_mappers = None
 
-    def test_double_same_mappers_flag_alias(self):
-        """test aliasing of joins with a custom join condition"""
-
-        (
-            addresses,
-            items,
-            order_items,
-            orders,
-            Item,
-            User,
-            Address,
-            Order,
-            users,
-        ) = (
-            self.tables.addresses,
-            self.tables.items,
-            self.tables.order_items,
-            self.tables.orders,
-            self.classes.Item,
-            self.classes.User,
-            self.classes.Address,
-            self.classes.Order,
-            self.tables.users,
-        )
-
-        self.mapper_registry.map_imperatively(Address, addresses)
-        self.mapper_registry.map_imperatively(
-            Order,
-            orders,
-            properties={
-                "items": relationship(
-                    Item,
-                    secondary=order_items,
-                    lazy="select",
-                    order_by=items.c.id,
-                )
-            },
-        )
-        self.mapper_registry.map_imperatively(Item, items)
-        self.mapper_registry.map_imperatively(
-            User,
-            users,
-            properties=dict(
-                addresses=relationship(Address, lazy="select"),
-                open_orders=relationship(
-                    Order,
-                    primaryjoin=and_(
-                        orders.c.isopen == 1, users.c.id == orders.c.user_id
-                    ),
-                    lazy="select",
-                    viewonly=True,
-                ),
-                closed_orders=relationship(
-                    Order,
-                    primaryjoin=and_(
-                        orders.c.isopen == 0, users.c.id == orders.c.user_id
-                    ),
-                    lazy="select",
-                    viewonly=True,
-                ),
-            ),
-        )
-        q = fixture_session().query(User)
-
-        eq_(
-            q.join("open_orders", "items", aliased=True)
-            .filter(Item.id == 4)
-            .join("closed_orders", "items", aliased=True)
-            .filter(Item.id == 3)
-            .all(),
-            [User(id=7)],
-        )
-
     def test_double_same_mappers_explicit_alias(self):
         """test aliasing of joins with a custom join condition"""
 
@@ -3614,7 +3542,7 @@ class ExternalColumnsTest(QueryTest):
 
         sess = fixture_session()
 
-        sess.query(Address).options(joinedload("user")).all()
+        sess.query(Address).options(joinedload(Address.user)).all()
 
         eq_(
             sess.query(User).all(),
@@ -3647,7 +3575,7 @@ class ExternalColumnsTest(QueryTest):
             def go():
                 eq_(
                     sess.query(Address)
-                    .options(joinedload("user"))
+                    .options(joinedload(Address.user))
                     .order_by(Address.id)
                     .all(),
                     address_result,
@@ -3657,15 +3585,15 @@ class ExternalColumnsTest(QueryTest):
 
         ualias = aliased(User)
         eq_(
-            sess.query(Address, ualias).join(ualias, "user").all(),
+            sess.query(Address, ualias).join(ualias, Address.user).all(),
             [(address, address.user) for address in address_result],
         )
 
         ualias2 = aliased(User)
         eq_(
             sess.query(Address, ualias.count)
-            .join(ualias, "user")
-            .join(ualias2, "user")
+            .join(ualias, Address.user)
+            .join(ualias2, Address.user)
             .order_by(Address.id)
             .all(),
             [
@@ -3679,8 +3607,8 @@ class ExternalColumnsTest(QueryTest):
 
         eq_(
             sess.query(Address, ualias.concat, ualias.count)
-            .join(ualias, "user")
-            .join(ualias2, "user")
+            .join(Address.user.of_type(ualias))
+            .join(Address.user.of_type(ualias2))
             .order_by(Address.id)
             .all(),
             [
@@ -3711,7 +3639,7 @@ class ExternalColumnsTest(QueryTest):
         eq_(
             list(
                 sess.query(Address)
-                .join("user")
+                .join(Address.user)
                 .with_entities(Address.id, User.id, User.concat, User.count)
             ),
             [
@@ -3726,7 +3654,7 @@ class ExternalColumnsTest(QueryTest):
         eq_(
             list(
                 sess.query(Address, ua)
-                .select_entity_from(join(Address, ua, "user"))
+                .select_entity_from(join(Address, ua, Address.user))
                 .with_entities(Address.id, ua.id, ua.concat, ua.count)
             ),
             [
@@ -3782,7 +3710,7 @@ class ExternalColumnsTest(QueryTest):
         def go():
             o1 = (
                 sess.query(Order)
-                .options(joinedload("address").joinedload("user"))
+                .options(joinedload(Order.address).joinedload(Address.user))
                 .get(1)
             )
             eq_(o1.address.user.count, 1)
@@ -3794,7 +3722,7 @@ class ExternalColumnsTest(QueryTest):
         def go():
             o1 = (
                 sess.query(Order)
-                .options(joinedload("address").joinedload("user"))
+                .options(joinedload(Order.address).joinedload(Address.user))
                 .first()
             )
             eq_(o1.address.user.count, 1)
index 1a40447a6dda065b9b487470dbad98b23a3d4081..0713462775962079db0222ea28787fc70afc881f 100644 (file)
@@ -264,11 +264,13 @@ class RelationshipsTest(_fixtures.FixtureTest):
         """Query.join"""
 
         User, Address = self.classes.User, self.classes.Address
+        Order = self.classes.Order
 
         session = fixture_session()
         q = (
             session.query(User)
-            .join("orders", "addresses")
+            .outerjoin(User.orders)
+            .outerjoin(Order.addresses)
             .filter(Address.id == 1)
         )
         eq_([User(id=7)], q.all())
@@ -285,7 +287,8 @@ class RelationshipsTest(_fixtures.FixtureTest):
         session = fixture_session()
         q = (
             session.query(User)
-            .outerjoin("orders", "addresses")
+            .outerjoin(User.orders)
+            .outerjoin(Order.addresses)
             .filter(sa.or_(Order.id == None, Address.id == 1))
         )  # noqa
         eq_(set([User(id=7), User(id=8), User(id=10)]), set(q.all()))
@@ -303,7 +306,8 @@ class RelationshipsTest(_fixtures.FixtureTest):
 
         q = (
             session.query(User)
-            .outerjoin("orders", "addresses")
+            .outerjoin(User.orders)
+            .outerjoin(Order.addresses)
             .filter(sa.or_(Order.id == None, Address.id == 1))
         )  # noqa
         eq_(q.count(), 4)
index eb7dfd6f38726c18374edf73a9267328af23843d..42f9b9aa16c94a216f8f8e12c40b3429807bd540 100644 (file)
@@ -226,8 +226,6 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
                 [
                     "relationship",
                     "relationship_only",
-                    "string_relationship",
-                    "string_relationship_only",
                     "none",
                     "explicit",
                     "table_none",
@@ -235,11 +233,6 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
                 ],
                 [True, False],
             )
-        ).difference(
-            [
-                ("string_relationship", False),
-                ("string_relationship_only", False),
-            ]
         ),
         argnames="onclause_type, use_legacy",
     )
@@ -256,12 +249,8 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
 
         if onclause_type == "relationship":
             q = q.join(Address, User.addresses)
-        elif onclause_type == "string_relationship":
-            q = q.join(Address, "addresses")
         elif onclause_type == "relationship_only":
             q = q.join(User.addresses)
-        elif onclause_type == "string_relationship_only":
-            q = q.join("addresses")
         elif onclause_type == "none":
             q = q.join(Address)
         elif onclause_type == "explicit":
index 457cfee4e2aaf40601a9850734c1ae794474b309..2bea9b110675f185e03a8479d951fb890f7855d5 100644 (file)
@@ -658,14 +658,14 @@ class LazyTest(_fixtures.FixtureTest):
             [Order(id=1), Order(id=5)],
             fixture_session()
             .query(closed_mapper)
-            .with_parent(user, property="closed_orders")
+            .with_parent(user, property=User.closed_orders)
             .all(),
         )
         eq_(
             [Order(id=3)],
             fixture_session()
             .query(open_mapper)
-            .with_parent(user, property="open_orders")
+            .with_parent(user, property=User.open_orders)
             .all(),
         )
 
@@ -720,7 +720,7 @@ class LazyTest(_fixtures.FixtureTest):
 
         eq_(
             self.static.item_keyword_result[0:2],
-            q.join("keywords").filter(keywords.c.name == "red").all(),
+            q.join(Item.keywords).filter(keywords.c.name == "red").all(),
         )
 
     def test_uses_get(self):
index 6c8b26c71104d33daeea0339ffde9090e4775fef..7d9e6f9c38a479ca2a5ecc62efb7c53652833866 100644 (file)
@@ -1498,7 +1498,7 @@ class MapperTest(_fixtures.FixtureTest, AssertsCompiledSQL):
         session = fixture_session()
         q = (
             session.query(Item)
-            .join("keywords")
+            .join(Item.keywords)
             .distinct()
             .filter(Keyword.name == "red")
         )
@@ -2376,7 +2376,7 @@ class RequirementsTest(fixtures.MappedTest):
         h1.h2s.extend([H2("abc"), H2("def")])
         s.flush()
 
-        h1s = s.query(H1).options(sa.orm.joinedload("h2s")).all()
+        h1s = s.query(H1).options(sa.orm.joinedload(H1.h2s)).all()
         eq_(len(h1s), 5)
 
         self.assert_unordered_result(
@@ -2394,15 +2394,15 @@ class RequirementsTest(fixtures.MappedTest):
             {"h2s": (H2, [{"value": "def"}])},
         )
 
-        h1s = s.query(H1).options(sa.orm.joinedload("h3s")).all()
+        h1s = s.query(H1).options(sa.orm.joinedload(H1.h3s)).all()
 
         eq_(len(h1s), 5)
         h1s = (
             s.query(H1)
             .options(
-                sa.orm.joinedload("t6a").joinedload("h1b"),
-                sa.orm.joinedload("h2s"),
-                sa.orm.joinedload("h3s").joinedload("h1s"),
+                sa.orm.joinedload(H1.t6a).joinedload(H6.h1b),
+                sa.orm.joinedload(H1.h2s),
+                sa.orm.joinedload(H1.h3s).joinedload(H3.h1s),
             )
             .all()
         )
@@ -3151,13 +3151,13 @@ class ConfigureOrNotConfigureTest(_fixtures.FixtureTest, AssertsCompiledSQL):
 
         if use_bound:
             stmt = select(User).options(
-                Load(User).load_only("name"),
+                Load(User).load_only(User.name),
             )
 
             is_true(um.configured)
         else:
             stmt = select(User).options(
-                load_only("name"),
+                load_only(User.name),
             )
             is_false(um.configured)
 
index 7d61a872227838c9fb7c7c353ddd24826b1b3375..3b97bd5a579e5429751c16bb57a4c95a31085312 100644 (file)
@@ -1211,7 +1211,9 @@ class MergeTest(_fixtures.FixtureTest):
             sess.commit()
 
         sess2 = fixture_session()
-        u2 = sess2.query(User).options(sa.orm.joinedload("addresses")).get(7)
+        u2 = (
+            sess2.query(User).options(sa.orm.joinedload(User.addresses)).get(7)
+        )
 
         sess3 = fixture_session()
         u3 = sess3.merge(u2, load=False)  # noqa
index bc32e322d4873dbb2c1b7c7b4e3d8faf184f7986..bdf7ab85923164eb7c3c09148f9c6aa2f811ac04 100644 (file)
@@ -81,7 +81,8 @@ class _PolymorphicTestBase(object):
         sess = fixture_session()
         eq_(
             sess.query(Company)
-            .join(Company.employees.of_type(Engineer), "machines")
+            .join(Company.employees.of_type(Engineer))
+            .join(Engineer.machines)
             .filter(Machine.name.ilike("%thinkpad%"))
             .all(),
             [self.c1],
index ebbbe738de246f7ce1ac71ee3a81679b9ee283dc..1a2a5ba70f9a4267603fcb8251d7445dbd2d3f7f 100644 (file)
@@ -509,15 +509,6 @@ class OptionsTest(PathTest, QueryTest):
             strategy_options._UnboundLoad.joinedload, arg, True, {}
         )
 
-    def test_get_path_one_level_string(self):
-        User = self.classes.User
-
-        sess = fixture_session()
-        q = sess.query(User)
-
-        opt = self._option_fixture("addresses")
-        self._assert_path_result(opt, q, [(User, "addresses")])
-
     def test_get_path_one_level_attribute(self):
         User = self.classes.User
 
@@ -545,33 +536,13 @@ class OptionsTest(PathTest, QueryTest):
 
     def test_get_path_one_level_with_unrelated(self):
         Order = self.classes.Order
+        User = self.classes.User
 
         sess = fixture_session()
         q = sess.query(Order)
-        opt = self._option_fixture("addresses")
+        opt = self._option_fixture(User.addresses)
         self._assert_path_result(opt, q, [])
 
-    def test_path_multilevel_string(self):
-        Item, User, Order = (
-            self.classes.Item,
-            self.classes.User,
-            self.classes.Order,
-        )
-
-        sess = fixture_session()
-        q = sess.query(User)
-
-        opt = self._option_fixture("orders.items.keywords")
-        self._assert_path_result(
-            opt,
-            q,
-            [
-                (User, "orders"),
-                (User, "orders", Order, "items"),
-                (User, "orders", Order, "items", Item, "keywords"),
-            ],
-        )
-
     def test_path_multilevel_attribute(self):
         Item, User, Order = (
             self.classes.Item,
@@ -593,21 +564,6 @@ class OptionsTest(PathTest, QueryTest):
             ],
         )
 
-    def test_with_current_matching_string(self):
-        Item, User, Order = (
-            self.classes.Item,
-            self.classes.User,
-            self.classes.Order,
-        )
-
-        sess = fixture_session()
-        q = sess.query(Item)._with_current_path(
-            self._make_path_registry([User, "orders", Order, "items"])
-        )
-
-        opt = self._option_fixture("orders.items.keywords")
-        self._assert_path_result(opt, q, [(Item, "keywords")])
-
     def test_with_current_matching_attribute(self):
         Item, User, Order = (
             self.classes.Item,
@@ -623,24 +579,6 @@ class OptionsTest(PathTest, QueryTest):
         opt = self._option_fixture(User.orders, Order.items, Item.keywords)
         self._assert_path_result(opt, q, [(Item, "keywords")])
 
-    def test_with_current_nonmatching_string(self):
-        Item, User, Order = (
-            self.classes.Item,
-            self.classes.User,
-            self.classes.Order,
-        )
-
-        sess = fixture_session()
-        q = sess.query(Item)._with_current_path(
-            self._make_path_registry([User, "orders", Order, "items"])
-        )
-
-        opt = self._option_fixture("keywords")
-        self._assert_path_result(opt, q, [])
-
-        opt = self._option_fixture("items.keywords")
-        self._assert_path_result(opt, q, [])
-
     def test_with_current_nonmatching_attribute(self):
         Item, User, Order = (
             self.classes.Item,
@@ -923,14 +861,6 @@ class OptionsTest(PathTest, QueryTest):
         q = sess.query(Item, Order)
         self._assert_path_result(opt, q, [(Order, "items")])
 
-    def test_multi_entity_opt_on_string(self):
-        Item = self.classes.Item
-        Order = self.classes.Order
-        opt = self._option_fixture("items")
-        sess = fixture_session()
-        q = sess.query(Item, Order)
-        self._assert_path_result(opt, q, [])
-
     def test_multi_entity_no_mapped_entities(self):
         Item = self.classes.Item
         Order = self.classes.Order
@@ -955,28 +885,11 @@ class OptionsTest(PathTest, QueryTest):
         Order = self.classes.Order
         sess = fixture_session()
         q = sess.query(User)
-        opt = self._option_fixture(User.orders).joinedload("items")
+        opt = self._option_fixture(User.orders).joinedload(Order.items)
         self._assert_path_result(
             opt, q, [(User, "orders"), (User, "orders", Order, "items")]
         )
 
-    def test_chained_plus_dotted(self):
-        User = self.classes.User
-        Order = self.classes.Order
-        Item = self.classes.Item
-        sess = fixture_session()
-        q = sess.query(User)
-        opt = self._option_fixture("orders.items").joinedload("keywords")
-        self._assert_path_result(
-            opt,
-            q,
-            [
-                (User, "orders"),
-                (User, "orders", Order, "items"),
-                (User, "orders", Order, "items", Item, "keywords"),
-            ],
-        )
-
     def test_chained_plus_multi(self):
         User = self.classes.User
         Order = self.classes.Order
@@ -984,7 +897,7 @@ class OptionsTest(PathTest, QueryTest):
         sess = fixture_session()
         q = sess.query(User)
         opt = self._option_fixture(User.orders, Order.items).joinedload(
-            "keywords"
+            Item.keywords
         )
         self._assert_path_result(
             opt,
@@ -1055,47 +968,21 @@ class OptionsNoPropTest(_fixtures.FixtureTest):
     run_inserts = None
     run_deletes = None
 
-    def test_option_with_mapper_basestring(self):
-        Item = self.classes.Item
-
-        self._assert_option([Item], "keywords")
-
     def test_option_with_mapper_PropCompatator(self):
         Item = self.classes.Item
 
         self._assert_option([Item], Item.keywords)
 
-    def test_option_with_mapper_then_column_basestring(self):
-        Item = self.classes.Item
-
-        self._assert_option([Item, Item.id], "keywords")
-
     def test_option_with_mapper_then_column_PropComparator(self):
         Item = self.classes.Item
 
         self._assert_option([Item, Item.id], Item.keywords)
 
-    def test_option_with_column_then_mapper_basestring(self):
-        Item = self.classes.Item
-
-        self._assert_option([Item.id, Item], "keywords")
-
     def test_option_with_column_then_mapper_PropComparator(self):
         Item = self.classes.Item
 
         self._assert_option([Item.id, Item], Item.keywords)
 
-    def test_option_with_column_basestring(self):
-        Item = self.classes.Item
-
-        message = (
-            "Query has only expression-based entities - can't "
-            'find property named "keywords".'
-        )
-        self._assert_eager_with_just_column_exception(
-            Item.id, "keywords", message
-        )
-
     def test_option_with_column_PropComparator(self):
         Item = self.classes.Item
 
@@ -1118,81 +1005,6 @@ class OptionsNoPropTest(_fixtures.FixtureTest):
             "the root entities to the target attribute. ",
         )
 
-    def test_option_against_nonexistent_basestring(self):
-        Item = self.classes.Item
-        self._assert_eager_with_entity_exception(
-            [Item],
-            (joinedload("foo"),),
-            'Can\'t find property named "foo" on mapped class '
-            "Item->items in this Query.",
-        )
-
-    def test_option_against_nonexistent_twolevel_basestring(self):
-        Item = self.classes.Item
-        self._assert_eager_with_entity_exception(
-            [Item],
-            (joinedload("keywords.foo"),),
-            'Can\'t find property named "foo" on mapped class '
-            "Keyword->keywords in this Query.",
-        )
-
-    def test_option_against_nonexistent_twolevel_chained(self):
-        Item = self.classes.Item
-        self._assert_eager_with_entity_exception(
-            [Item],
-            (joinedload("keywords").joinedload("foo"),),
-            'Can\'t find property named "foo" on mapped class '
-            "Keyword->keywords in this Query.",
-        )
-
-    @testing.fails_if(
-        lambda: True,
-        "PropertyOption doesn't yet check for relation/column on end result",
-    )
-    def test_option_against_non_relation_basestring(self):
-        Item = self.classes.Item
-        Keyword = self.classes.Keyword
-        self._assert_eager_with_entity_exception(
-            [Keyword, Item],
-            (joinedload("keywords"),),
-            r"Attribute 'keywords' of entity 'Mapper\|Keyword\|keywords' "
-            "does not refer to a mapped entity",
-        )
-
-    @testing.fails_if(
-        lambda: True,
-        "PropertyOption doesn't yet check for relation/column on end result",
-    )
-    def test_option_against_multi_non_relation_basestring(self):
-        Item = self.classes.Item
-        Keyword = self.classes.Keyword
-        self._assert_eager_with_entity_exception(
-            [Keyword, Item],
-            (joinedload("keywords"),),
-            r"Attribute 'keywords' of entity 'Mapper\|Keyword\|keywords' "
-            "does not refer to a mapped entity",
-        )
-
-    def test_option_against_wrong_entity_type_basestring(self):
-        Item = self.classes.Item
-        self._assert_loader_strategy_exception(
-            [Item],
-            (joinedload("id").joinedload("keywords"),),
-            'Can\'t apply "joined loader" strategy to property "Item.id", '
-            'which is a "column property"; this loader strategy is '
-            'intended to be used with a "relationship property".',
-        )
-
-    def test_col_option_against_relationship_basestring(self):
-        Item = self.classes.Item
-        self._assert_loader_strategy_exception(
-            [Item],
-            (load_only("keywords"),),
-            'Can\'t apply "column loader" strategy to property '
-            '"Item.keywords", which is a "relationship property"; this '
-            'loader strategy is intended to be used with a "column property".',
-        )
-
     def test_load_only_against_multi_entity_attr(self):
         User = self.classes.User
         Item = self.classes.Item
@@ -1217,37 +1029,6 @@ class OptionsNoPropTest(_fixtures.FixtureTest):
             'loader strategy is intended to be used with a "column property".',
         )
 
-    def test_option_against_multi_non_relation_twolevel_basestring(self):
-        Item = self.classes.Item
-        Keyword = self.classes.Keyword
-        self._assert_loader_strategy_exception(
-            [Keyword, Item],
-            (joinedload("id").joinedload("keywords"),),
-            'Can\'t apply "joined loader" strategy to property "Keyword.id", '
-            'which is a "column property"; this loader strategy is intended '
-            'to be used with a "relationship property".',
-        )
-
-    def test_option_against_multi_nonexistent_basestring(self):
-        Item = self.classes.Item
-        Keyword = self.classes.Keyword
-        self._assert_eager_with_entity_exception(
-            [Keyword, Item],
-            (joinedload("description"),),
-            'Can\'t find property named "description" on mapped class '
-            "Keyword->keywords in this Query.",
-        )
-
-    def test_option_against_multi_no_entities_basestring(self):
-        Item = self.classes.Item
-        Keyword = self.classes.Keyword
-        self._assert_eager_with_entity_exception(
-            [Keyword.id, Item.id],
-            (joinedload("keywords"),),
-            r"Query has only expression-based entities - can't find property "
-            'named "keywords".',
-        )
-
     def test_option_against_wrong_multi_entity_type_attr_one(self):
         Item = self.classes.Item
         Keyword = self.classes.Keyword
@@ -1467,22 +1248,6 @@ class OptionsNoPropTestInh(_Polymorphic):
             ._compile_state,
         )
 
-    def test_missing_str_attr_of_type_subclass(self):
-        s = fixture_session()
-
-        assert_raises_message(
-            sa.exc.ArgumentError,
-            r'Can\'t find property named "manager_name" on '
-            r"mapped class Engineer->engineers in this Query.$",
-            s.query(Company)
-            .options(
-                joinedload(Company.employees.of_type(Engineer)).load_only(
-                    "manager_name"
-                )
-            )
-            ._compile_state,
-        )
-
     def test_missing_attr_of_type_wpoly_subclass(self):
         s = fixture_session()
 
@@ -1883,35 +1648,6 @@ class SubOptionsTest(PathTest, QueryTest):
         sess = fixture_session()
         self._assert_opts(sess.query(User), sub_opt, non_sub_opts)
 
-    def test_four_strings(self):
-        User, Address, Order, Item, SubItem, Keyword = self.classes(
-            "User", "Address", "Order", "Item", "SubItem", "Keyword"
-        )
-        sub_opt = joinedload("orders").options(
-            defer("description"),
-            joinedload("items").options(
-                joinedload("keywords").options(defer("name")),
-                defer("description"),
-            ),
-        )
-        non_sub_opts = [
-            joinedload(User.orders),
-            defaultload(User.orders).defer(Order.description),
-            defaultload(User.orders).joinedload(Order.items),
-            defaultload(User.orders)
-            .defaultload(Order.items)
-            .joinedload(Item.keywords),
-            defaultload(User.orders)
-            .defaultload(Order.items)
-            .defer(Item.description),
-            defaultload(User.orders)
-            .defaultload(Order.items)
-            .defaultload(Item.keywords)
-            .defer(Keyword.name),
-        ]
-        sess = fixture_session()
-        self._assert_opts(sess.query(User), sub_opt, non_sub_opts)
-
     def test_five(self):
         User, Address, Order, Item, SubItem, Keyword = self.classes(
             "User", "Address", "Order", "Item", "SubItem", "Keyword"
@@ -1924,18 +1660,6 @@ class SubOptionsTest(PathTest, QueryTest):
         sess = fixture_session()
         self._assert_opts(sess.query(User), sub_opt, non_sub_opts)
 
-    def test_five_strings(self):
-        User, Address, Order, Item, SubItem, Keyword = self.classes(
-            "User", "Address", "Order", "Item", "SubItem", "Keyword"
-        )
-        sub_opt = joinedload("orders").options(load_only("description"))
-        non_sub_opts = [
-            joinedload(User.orders),
-            defaultload(User.orders).load_only(Order.description),
-        ]
-        sess = fixture_session()
-        self._assert_opts(sess.query(User), sub_opt, non_sub_opts)
-
     def test_invalid_one(self):
         User, Address, Order, Item, SubItem = self.classes(
             "User", "Address", "Order", "Item", "SubItem"
@@ -1956,26 +1680,6 @@ class SubOptionsTest(PathTest, QueryTest):
         sess = fixture_session()
         self._assert_opts(sess.query(User), sub_opt, non_sub_opts)
 
-    def test_invalid_two(self):
-        User, Address, Order, Item, SubItem = self.classes(
-            "User", "Address", "Order", "Item", "SubItem"
-        )
-
-        # these options are "invalid", in that User.orders -> Item.keywords
-        # is not a path.  However, the "normal" option is not generating
-        # an error for now, which is bad, but we're testing here only that
-        # it works the same way, so there you go.   If and when we make this
-        # case raise, then both cases should raise in the same way.
-        sub_opt = joinedload("orders").options(
-            joinedload("keywords"), joinedload("items")
-        )
-        non_sub_opts = [
-            joinedload(User.orders).joinedload(Item.keywords),
-            defaultload(User.orders).joinedload(Order.items),
-        ]
-        sess = fixture_session()
-        self._assert_opts(sess.query(User), sub_opt, non_sub_opts)
-
     def test_not_implemented_fromload(self):
         User, Address, Order, Item, SubItem = self.classes(
             "User", "Address", "Order", "Item", "SubItem"
@@ -2030,7 +1734,7 @@ class MapperOptionsTest(_fixtures.FixtureTest):
             u = (
                 sess.query(User)
                 .order_by(User.id)
-                .options(sa.orm.joinedload("adlist"))
+                .options(sa.orm.joinedload(User.adlist))
                 .filter_by(name="jack")
             ).one()
             eq_(u.adlist, [self.static.user_address_result[0].addresses[0]])
index e33ec9a5c5a52e2d23edff91916149a3053e715a..e6f15c47b53d16cb1aeeae95a5b141b6ded7dc00 100644 (file)
@@ -345,8 +345,10 @@ class PickleTest(fixtures.MappedTest):
             u1 = (
                 sess.query(User)
                 .options(
-                    sa.orm.defer("name"),
-                    sa.orm.defer("addresses.email_address"),
+                    sa.orm.defer(User.name),
+                    sa.orm.defaultload(User.addresses).defer(
+                        Address.email_address
+                    ),
                 )
                 .get(u1.id)
             )
@@ -496,13 +498,11 @@ class PickleTest(fixtures.MappedTest):
     @testing.requires.non_broken_pickle
     @testing.combinations(
         lambda User: sa.orm.joinedload(User.addresses),
-        lambda: sa.orm.joinedload("addresses"),
-        lambda: sa.orm.defer("name"),
         lambda User: sa.orm.defer(User.name),
-        lambda Address: sa.orm.joinedload("addresses").joinedload(
+        lambda Address: sa.orm.joinedload(User.addresses).joinedload(
             Address.dingaling
         ),
-        lambda: sa.orm.joinedload("addresses").raiseload("*"),
+        lambda: sa.orm.joinedload(User.addresses).raiseload("*"),
         lambda: sa.orm.raiseload("*"),
     )
     def test_unbound_options(self, test_case):
@@ -518,15 +518,15 @@ class PickleTest(fixtures.MappedTest):
     @testing.requires.non_broken_pickle
     @testing.combinations(
         lambda User: sa.orm.Load(User).joinedload(User.addresses),
-        lambda User: sa.orm.Load(User).joinedload("addresses"),
-        lambda User: sa.orm.Load(User).joinedload("addresses").raiseload("*"),
-        lambda User: sa.orm.Load(User).defer("name"),
+        lambda User: sa.orm.Load(User)
+        .joinedload(User.addresses)
+        .raiseload("*"),
         lambda User: sa.orm.Load(User).defer(User.name),
         lambda User, Address: sa.orm.Load(User)
-        .joinedload("addresses")
+        .joinedload(User.addresses)
         .joinedload(Address.dingaling),
         lambda User, Address: sa.orm.Load(User)
-        .joinedload("addresses", innerjoin=True)
+        .joinedload(User.addresses, innerjoin=True)
         .joinedload(Address.dingaling),
     )
     def test_bound_options(self, test_case):
@@ -548,10 +548,8 @@ class PickleTest(fixtures.MappedTest):
 
         for opt in [
             sa.orm.joinedload(User.addresses),
-            sa.orm.joinedload("addresses"),
-            sa.orm.defer("name"),
             sa.orm.defer(User.name),
-            sa.orm.joinedload("addresses").joinedload(Address.dingaling),
+            sa.orm.joinedload(User.addresses).joinedload(Address.dingaling),
         ]:
             context = sess.query(User).options(opt)._compile_context()
             opt = [
index 3e58f211c489651a2dc7b6f6aa0a7590f14f8f85..c9353f5511231d14cd3021bd1b7729a594c25b44 100644 (file)
@@ -1340,7 +1340,7 @@ class GetTest(QueryTest):
 
         s = fixture_session()
 
-        q = s.query(User).join("addresses").filter(Address.user_id == 8)
+        q = s.query(User).join(User.addresses).filter(Address.user_id == 8)
         assert_raises(sa_exc.InvalidRequestError, q.get, 7)
         assert_raises(
             sa_exc.InvalidRequestError,
@@ -1361,7 +1361,7 @@ class GetTest(QueryTest):
 
         s.query(User).get(7)
 
-        q = s.query(User).join("addresses").filter(Address.user_id == 8)
+        q = s.query(User).join(User.addresses).filter(Address.user_id == 8)
         assert_raises(sa_exc.InvalidRequestError, q.get, 7)
 
     def test_unique_param_names(self):
@@ -1428,6 +1428,7 @@ class GetTest(QueryTest):
 
     def test_populate_existing(self):
         User, Address = self.classes.User, self.classes.Address
+        Order = self.classes.Order
 
         s = fixture_session(autoflush=False)
 
@@ -1457,13 +1458,15 @@ class GetTest(QueryTest):
 
         # eager load does
         s.query(User).options(
-            joinedload("addresses"), joinedload("orders").joinedload("items")
+            joinedload(User.addresses),
+            joinedload(User.orders).joinedload(Order.items),
         ).populate_existing().all()
         assert u.addresses[0].email_address == "jack@bean.com"
         assert u.orders[1].items[2].description == "item 5"
 
     def test_populate_existing_future(self):
         User, Address = self.classes.User, self.classes.Address
+        Order = self.classes.Order
 
         s = fixture_session(autoflush=False)
 
@@ -1500,8 +1503,8 @@ class GetTest(QueryTest):
         stmt = (
             select(User)
             .options(
-                joinedload("addresses"),
-                joinedload("orders").joinedload("items"),
+                joinedload(User.addresses),
+                joinedload(User.orders).joinedload(Order.items),
             )
             .execution_options(populate_existing=True)
         )
@@ -1545,7 +1548,7 @@ class InvalidGenerationsTest(QueryTest, AssertsCompiledSQL):
 
         q = testing.resolve_lambda(test_case, User=User, s=s)
 
-        assert_raises(sa_exc.InvalidRequestError, q.join, "addresses")
+        assert_raises(sa_exc.InvalidRequestError, q.join, User.addresses)
 
         assert_raises(sa_exc.InvalidRequestError, q.filter, User.name == "ed")
 
@@ -1557,7 +1560,7 @@ class InvalidGenerationsTest(QueryTest, AssertsCompiledSQL):
 
         assert_raises(sa_exc.InvalidRequestError, q.having, "foo")
 
-        q.enable_assertions(False).join("addresses")
+        q.enable_assertions(False).join(User.addresses)
         q.enable_assertions(False).filter(User.name == "ed")
         q.enable_assertions(False).order_by("foo")
         q.enable_assertions(False).group_by("foo")
@@ -1570,7 +1573,7 @@ class InvalidGenerationsTest(QueryTest, AssertsCompiledSQL):
         q = s.query(User).select_from(users)
         assert_raises(sa_exc.InvalidRequestError, q.select_from, users)
 
-        q = s.query(User).join("addresses")
+        q = s.query(User).join(User.addresses)
         assert_raises(sa_exc.InvalidRequestError, q.select_from, users)
 
         q = s.query(User).order_by(User.id)
@@ -3360,13 +3363,13 @@ class FilterTest(QueryTest, AssertsCompiledSQL):
         # test that the contents are not adapted by the aliased join
         ua = aliased(Address)
         assert [User(id=7), User(id=8)] == sess.query(User).join(
-            ua, "addresses"
+            ua, User.addresses
         ).filter(
             ~User.addresses.any(Address.email_address == "fred@fred.com")
         ).all()
 
         assert [User(id=10)] == sess.query(User).outerjoin(
-            ua, "addresses"
+            ua, User.addresses
         ).filter(~User.addresses.any()).all()
 
     def test_any_doesnt_overcorrelate(self):
@@ -3378,7 +3381,7 @@ class FilterTest(QueryTest, AssertsCompiledSQL):
 
         # test that any() doesn't overcorrelate
         assert [User(id=7), User(id=8)] == sess.query(User).join(
-            "addresses"
+            User.addresses
         ).filter(
             ~User.addresses.any(Address.email_address == "fred@fred.com")
         ).all()
@@ -3417,7 +3420,7 @@ class FilterTest(QueryTest, AssertsCompiledSQL):
         # test has() doesn't overcorrelate
         assert [Address(id=2), Address(id=3), Address(id=4)] == sess.query(
             Address
-        ).join("user").filter(
+        ).join(Address.user).filter(
             Address.user.has(User.name.like("%ed%"), id=8)
         ).order_by(
             Address.id
@@ -3427,7 +3430,7 @@ class FilterTest(QueryTest, AssertsCompiledSQL):
         ua = aliased(User)
         assert [Address(id=2), Address(id=3), Address(id=4)] == sess.query(
             Address
-        ).join(ua, "user").filter(
+        ).join(ua, Address.user).filter(
             Address.user.has(User.name.like("%ed%"), id=8)
         ).order_by(
             Address.id
@@ -4409,7 +4412,7 @@ class AggregateTest(QueryTest):
         sess = fixture_session()
         assert [User(name="ed", id=8)] == sess.query(User).order_by(
             User.id
-        ).group_by(User).join("addresses").having(
+        ).group_by(User).join(User.addresses).having(
             func.count(Address.id) > 2
         ).all()
 
@@ -4417,7 +4420,7 @@ class AggregateTest(QueryTest):
             User(name="jack", id=7),
             User(name="fred", id=9),
         ] == sess.query(User).order_by(User.id).group_by(User).join(
-            "addresses"
+            User.addresses
         ).having(
             func.count(Address.id) < 2
         ).all()
@@ -4828,7 +4831,7 @@ class DistinctTest(QueryTest, AssertsCompiledSQL):
 
         subq = (
             sess.query(User, Address.email_address)
-            .join("addresses")
+            .join(User.addresses)
             .distinct()
             .subquery()
         )
@@ -4850,7 +4853,7 @@ class DistinctTest(QueryTest, AssertsCompiledSQL):
         sess = fixture_session()
         q = (
             sess.query(User, Address.email_address)
-            .join("addresses")
+            .join(User.addresses)
             .distinct()
         )
 
@@ -4875,9 +4878,9 @@ class DistinctTest(QueryTest, AssertsCompiledSQL):
         # test that it works on embedded joinedload/LIMIT subquery
         q = (
             sess.query(User)
-            .join("addresses")
+            .join(User.addresses)
             .distinct()
-            .options(joinedload("addresses"))
+            .options(joinedload(User.addresses))
             .order_by(desc(Address.email_address))
             .limit(2)
         )
@@ -5227,9 +5230,9 @@ class DistinctTest(QueryTest, AssertsCompiledSQL):
 
         q = (
             sess.query(User)
-            .join("addresses")
+            .join(User.addresses)
             .distinct(Address.email_address)
-            .options(joinedload("addresses"))
+            .options(joinedload(User.addresses))
             .order_by(desc(Address.email_address))
             .limit(2)
         )
@@ -5407,7 +5410,7 @@ class YieldTest(_fixtures.FixtureTest):
 
         User = self.classes.User
         sess = fixture_session()
-        q = sess.query(User).options(joinedload("addresses")).yield_per(1)
+        q = sess.query(User).options(joinedload(User.addresses)).yield_per(1)
         assert_raises_message(
             sa_exc.InvalidRequestError,
             "Can't use yield_per with eager loaders that require "
@@ -5420,7 +5423,7 @@ class YieldTest(_fixtures.FixtureTest):
 
         User = self.classes.User
         sess = fixture_session()
-        q = sess.query(User).options(subqueryload("addresses")).yield_per(1)
+        q = sess.query(User).options(subqueryload(User.addresses)).yield_per(1)
         assert_raises_message(
             sa_exc.InvalidRequestError,
             "Can't use yield_per with eager loaders that require "
@@ -5455,7 +5458,7 @@ class YieldTest(_fixtures.FixtureTest):
         sess = fixture_session()
         q = (
             sess.query(User)
-            .options(subqueryload("addresses"))
+            .options(subqueryload(User.addresses))
             .enable_eagerloads(False)
             .yield_per(1)
         )
@@ -5463,7 +5466,7 @@ class YieldTest(_fixtures.FixtureTest):
 
         q = (
             sess.query(User)
-            .options(joinedload("addresses"))
+            .options(joinedload(User.addresses))
             .enable_eagerloads(False)
             .yield_per(1)
         )
@@ -5475,7 +5478,7 @@ class YieldTest(_fixtures.FixtureTest):
         sess = fixture_session()
         q = (
             sess.query(Address)
-            .options(lazyload("*"), joinedload("user"))
+            .options(lazyload("*"), joinedload(Address.user))
             .yield_per(1)
             .filter_by(id=1)
         )
@@ -6154,7 +6157,7 @@ class TextTest(QueryTest, AssertsCompiledSQL):
 
         q = (
             s.query(User)
-            .options(joinedload("addresses"))
+            .options(joinedload(User.addresses))
             .order_by(desc("name"))
             .limit(1)
         )
@@ -6172,7 +6175,7 @@ class TextTest(QueryTest, AssertsCompiledSQL):
 
         q = (
             s.query(User)
-            .options(joinedload("addresses"))
+            .options(joinedload(User.addresses))
             .order_by("name")
             .limit(1)
         )
@@ -6190,7 +6193,7 @@ class TextTest(QueryTest, AssertsCompiledSQL):
 
         self.assert_compile(
             s.query(User)
-            .options(joinedload("addresses"))
+            .options(joinedload(User.addresses))
             .order_by("users_name")
             .limit(1),
             "SELECT anon_1.users_id AS anon_1_users_id, "
@@ -6209,7 +6212,7 @@ class TextTest(QueryTest, AssertsCompiledSQL):
         # however! this works (again?)
         eq_(
             s.query(User)
-            .options(joinedload("addresses"))
+            .options(joinedload(User.addresses))
             .order_by("users_name")
             .first(),
             User(name="chuck", addresses=[]),
@@ -6222,7 +6225,7 @@ class TextTest(QueryTest, AssertsCompiledSQL):
 
         self.assert_compile(
             s.query(User)
-            .options(joinedload("addresses"))
+            .options(joinedload(User.addresses))
             .order_by(desc("users_name"))
             .limit(1),
             "SELECT anon_1.users_id AS anon_1_users_id, "
@@ -6241,7 +6244,7 @@ class TextTest(QueryTest, AssertsCompiledSQL):
         # however! this works (again?)
         eq_(
             s.query(User)
-            .options(joinedload("addresses"))
+            .options(joinedload(User.addresses))
             .order_by(desc("users_name"))
             .first(),
             User(name="jack", addresses=[Address()]),
@@ -6260,7 +6263,7 @@ class TextTest(QueryTest, AssertsCompiledSQL):
         q = sess.query(User, Address.email_address.label("email_address"))
 
         result = (
-            q.join("addresses")
+            q.join(User.addresses)
             .options(joinedload(User.orders))
             .order_by("email_address desc")
             .limit(1)
@@ -6330,13 +6333,6 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
         ] == o
 
         # test with explicit property
-        o = sess.query(Order).with_parent(u1, property="orders").all()
-        assert [
-            Order(description="order 1"),
-            Order(description="order 3"),
-            Order(description="order 5"),
-        ] == o
-
         o = sess.query(Order).with_parent(u1, property=User.orders).all()
         assert [
             Order(description="order 1"),
@@ -6385,7 +6381,7 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
         sess = fixture_session()
         u1 = sess.query(User).get(7)
         q = sess.query(User, Address).filter(
-            with_parent(u1, "addresses", from_entity=Address)
+            with_parent(u1, User.addresses, from_entity=Address)
         )
         self.assert_compile(
             q,
@@ -6404,7 +6400,7 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
         sess = fixture_session()
         u1 = sess.query(User).get(7)
         q = sess.query(User, Address).with_parent(
-            u1, "addresses", from_entity=Address
+            u1, User.addresses, from_entity=Address
         )
         self.assert_compile(
             q,
@@ -6440,7 +6436,7 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
         sess = fixture_session()
         u1 = sess.query(User).get(7)
         a1 = aliased(Address)
-        q = sess.query(a1).with_parent(u1, "addresses")
+        q = sess.query(a1).with_parent(u1, User.addresses)
         self.assert_compile(
             q,
             "SELECT addresses_1.id AS addresses_1_id, "
@@ -6530,7 +6526,7 @@ 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, "orders")
+        o = sess.query(Order).with_parent(utrans, User.orders)
         eq_(
             [
                 Order(description="order 1"),
@@ -6540,7 +6536,7 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
             o.all(),
         )
 
-        o = sess.query(Order).filter(with_parent(utrans, "orders"))
+        o = sess.query(Order).filter(with_parent(utrans, User.orders))
         eq_(
             [
                 Order(description="order 1"),
@@ -6559,11 +6555,11 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
         opending = Order(id=20, user_id=o1.user_id)
         sess.add(opending)
         eq_(
-            sess.query(User).with_parent(opending, "user").one(),
+            sess.query(User).with_parent(opending, Order.user).one(),
             User(id=o1.user_id),
         )
         eq_(
-            sess.query(User).filter(with_parent(opending, "user")).one(),
+            sess.query(User).filter(with_parent(opending, Order.user)).one(),
             User(id=o1.user_id),
         )
 
@@ -6576,7 +6572,7 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
         opending = Order(user_id=o1.user_id)
         sess.add(opending)
         eq_(
-            sess.query(User).with_parent(opending, "user").one(),
+            sess.query(User).with_parent(opending, Order.user).one(),
             User(id=o1.user_id),
         )
 
@@ -6587,8 +6583,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, "addresses")
-        q2 = sess.query(Address).with_parent(u2, "addresses")
+        q1 = sess.query(Address).with_parent(u1, User.addresses)
+        q2 = sess.query(Address).with_parent(u2, User.addresses)
 
         self.assert_compile(
             q1.union(q2),
@@ -6614,7 +6610,10 @@ class ParentTest(QueryTest, AssertsCompiledSQL):
 
         self.assert_compile(
             sess.query(Address).filter(
-                or_(with_parent(u1, "addresses"), with_parent(u2, "addresses"))
+                or_(
+                    with_parent(u1, User.addresses),
+                    with_parent(u2, User.addresses),
+                )
             ),
             "SELECT addresses.id AS addresses_id, addresses.user_id AS "
             "addresses_user_id, addresses.email_address AS "
@@ -6832,7 +6831,7 @@ class WithTransientOnNone(_fixtures.FixtureTest, AssertsCompiledSQL):
 
         sess = fixture_session()
 
-        q = sess.query(User).with_parent(Address(user_id=None), "user")
+        q = sess.query(User).with_parent(Address(user_id=None), Address.user)
         with expect_warnings("Got None for value of column"):
             self.assert_compile(
                 q,
@@ -6847,7 +6846,7 @@ class WithTransientOnNone(_fixtures.FixtureTest, AssertsCompiledSQL):
 
         s = fixture_session()
         q = s.query(User).with_parent(
-            Address(user_id=None, email_address=None), "special_user"
+            Address(user_id=None, email_address=None), Address.special_user
         )
         with expect_warnings("Got None for value of column"):
 
@@ -7025,35 +7024,6 @@ class SynonymTest(QueryTest, AssertsCompiledSQL):
 
         self.assert_sql_count(testing.db, go, 1)
 
-    def test_options_syn_of_syn_string(self):
-        User, Order = self.classes.User, self.classes.Order
-
-        s = fixture_session()
-
-        def go():
-            result = (
-                s.query(User)
-                .filter_by(name="jack")
-                .options(joinedload("orders_syn_2"))
-                .all()
-            )
-            eq_(
-                result,
-                [
-                    User(
-                        id=7,
-                        name="jack",
-                        orders=[
-                            Order(description="order 1"),
-                            Order(description="order 3"),
-                            Order(description="order 5"),
-                        ],
-                    )
-                ],
-            )
-
-        self.assert_sql_count(testing.db, go, 1)
-
     def test_joins(self):
         User, Order = self.classes.User, self.classes.Order
 
@@ -7084,12 +7054,12 @@ class SynonymTest(QueryTest, AssertsCompiledSQL):
         Order, User = self.classes.Order, self.classes.User
 
         for nameprop, orderprop in (
-            ("name", "orders"),
-            ("name_syn", "orders"),
-            ("name", "orders_syn"),
-            ("name", "orders_syn_2"),
-            ("name_syn", "orders_syn"),
-            ("name_syn", "orders_syn_2"),
+            ("name", User.orders),
+            ("name_syn", User.orders),
+            ("name", User.orders_syn),
+            ("name", User.orders_syn_2),
+            ("name_syn", User.orders_syn),
+            ("name_syn", User.orders_syn_2),
         ):
             with fixture_session() as sess:
                 q = sess.query(User)
index a25e39c82b7e62fd0575f16b4ba228ad00ac2c10..2547a981a22ac648cf977ac7202f710e73cf7570 100644 (file)
@@ -358,7 +358,7 @@ class M2ODontOverwriteFKTest(fixtures.MappedTest):
         sess.commit()
 
         # test that was broken by #3060
-        a1 = sess.query(A).options(joinedload("b")).first()
+        a1 = sess.query(A).options(joinedload(A.b)).first()
         a1.bid = b1.id
         sess.flush()
 
@@ -6081,7 +6081,11 @@ class RaiseLoadTest(_fixtures.FixtureTest):
             users,
             properties=dict(addresses=relationship(Address, lazy="raise")),
         )
-        q = fixture_session().query(User).options(sa.orm.lazyload("addresses"))
+        q = (
+            fixture_session()
+            .query(User)
+            .options(sa.orm.lazyload(User.addresses))
+        )
         result = [None]
 
         def go():
@@ -6110,7 +6114,7 @@ class RaiseLoadTest(_fixtures.FixtureTest):
         a1 = (
             s.query(Address)
             .filter_by(id=1)
-            .options(sa.orm.raiseload("user"))
+            .options(sa.orm.raiseload(Address.user))
             .first()
         )
 
@@ -6138,7 +6142,7 @@ class RaiseLoadTest(_fixtures.FixtureTest):
         a1 = (
             s.query(Address)
             .filter_by(id=1)
-            .options(sa.orm.raiseload("user", sql_only=True))
+            .options(sa.orm.raiseload(Address.user, sql_only=True))
             .first()
         )
 
@@ -6157,7 +6161,7 @@ class RaiseLoadTest(_fixtures.FixtureTest):
         a1 = (
             s.query(Address)
             .filter_by(id=1)
-            .options(sa.orm.raiseload("user", sql_only=True))
+            .options(sa.orm.raiseload(Address.user, sql_only=True))
             .first()
         )
         assert "user" not in a1.__dict__
@@ -6189,7 +6193,7 @@ class RaiseLoadTest(_fixtures.FixtureTest):
         a1 = (
             s.query(Address)
             .filter_by(id=1)
-            .options(sa.orm.raiseload("user", sql_only=True))
+            .options(sa.orm.raiseload(Address.user, sql_only=True))
             .first()
         )
 
index ceaead9c430ed8df1940e2d4d7d29074504db0ff..e3d852e6821a08fea0a91ff7b62eff0e483e726a 100644 (file)
@@ -352,7 +352,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL):
         def go():
             eq_(
                 self.static.item_keyword_result[0:2],
-                q.join("keywords").filter(Keyword.name == "red").all(),
+                q.join(Item.keywords).filter(Keyword.name == "red").all(),
             )
 
         self.assert_sql_count(testing.db, go, 2)
@@ -386,7 +386,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL):
             ka = aliased(Keyword)
             eq_(
                 self.static.item_keyword_result[0:2],
-                (q.join(ka, "keywords").filter(ka.name == "red")).all(),
+                (q.join(ka, Item.keywords).filter(ka.name == "red")).all(),
             )
 
         self.assert_sql_count(testing.db, go, 2)
@@ -1571,7 +1571,7 @@ class LoadOnExistingTest(_fixtures.FixtureTest):
         a2 = u1.addresses[0]
         a2.email_address = "foo"
         sess.query(User).options(
-            selectinload("addresses").selectinload("dingaling")
+            selectinload(User.addresses).selectinload(Address.dingaling)
         ).filter_by(id=8).all()
         assert u1.addresses[-1] is a1
         for a in u1.addresses:
@@ -1590,7 +1590,7 @@ class LoadOnExistingTest(_fixtures.FixtureTest):
         o1 = Order()
         u1.orders.append(o1)
         sess.query(User).options(
-            selectinload("orders").selectinload("items")
+            selectinload(User.orders).selectinload(Order.items)
         ).filter_by(id=7).all()
         for o in u1.orders:
             if o is not o1:
@@ -1604,11 +1604,11 @@ class LoadOnExistingTest(_fixtures.FixtureTest):
         u1 = (
             sess.query(User)
             .filter_by(id=8)
-            .options(selectinload("addresses"))
+            .options(selectinload(User.addresses))
             .one()
         )
         sess.query(User).filter_by(id=8).options(
-            selectinload("addresses").selectinload("dingaling")
+            selectinload(User.addresses).selectinload(Address.dingaling)
         ).first()
         assert "dingaling" in u1.addresses[0].__dict__
 
@@ -1618,11 +1618,11 @@ class LoadOnExistingTest(_fixtures.FixtureTest):
         u1 = (
             sess.query(User)
             .filter_by(id=7)
-            .options(selectinload("orders"))
+            .options(selectinload(User.orders))
             .one()
         )
         sess.query(User).filter_by(id=7).options(
-            selectinload("orders").selectinload("items")
+            selectinload(User.orders).selectinload(Order.items)
         ).first()
         assert "items" in u1.orders[0].__dict__
 
@@ -2784,7 +2784,7 @@ class SelfReferentialTest(fixtures.MappedTest):
             eq_(
                 Node(data="n1", children=[Node(data="n11"), Node(data="n12")]),
                 sess.query(Node)
-                .options(undefer("data"))
+                .options(undefer(Node.data))
                 .order_by(Node.id)
                 .first(),
             )
@@ -2797,7 +2797,10 @@ class SelfReferentialTest(fixtures.MappedTest):
             eq_(
                 Node(data="n1", children=[Node(data="n11"), Node(data="n12")]),
                 sess.query(Node)
-                .options(undefer("data"), undefer("children.data"))
+                .options(
+                    undefer(Node.data),
+                    defaultload(Node.children).undefer(Node.data),
+                )
                 .first(),
             )
 
@@ -2832,7 +2835,9 @@ class SelfReferentialTest(fixtures.MappedTest):
                 sess.query(Node)
                 .filter_by(data="n1")
                 .order_by(Node.id)
-                .options(selectinload("children").selectinload("children"))
+                .options(
+                    selectinload(Node.children).selectinload(Node.children)
+                )
                 .first()
             )
             eq_(
index 70d8d19bd5fcd4fbe8dec2c3cd2a92368077962e..25ebb9ebe4ceef108b8279be25aa5724706b9541 100644 (file)
@@ -10,6 +10,7 @@ from sqlalchemy.orm import aliased
 from sqlalchemy.orm import backref
 from sqlalchemy.orm import clear_mappers
 from sqlalchemy.orm import close_all_sessions
+from sqlalchemy.orm import defaultload
 from sqlalchemy.orm import defer
 from sqlalchemy.orm import deferred
 from sqlalchemy.orm import joinedload
@@ -443,7 +444,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL):
         def go():
             eq_(
                 self.static.item_keyword_result[0:2],
-                q.join("keywords").filter(Keyword.name == "red").all(),
+                q.join(Item.keywords).filter(Keyword.name == "red").all(),
             )
 
         self.assert_sql_count(testing.db, go, 2)
@@ -477,7 +478,7 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL):
             ka = aliased(Keyword)
             eq_(
                 self.static.item_keyword_result[0:2],
-                (q.join(ka, "keywords").filter(ka.name == "red")).all(),
+                (q.join(ka, Item.keywords).filter(ka.name == "red")).all(),
             )
 
         self.assert_sql_count(testing.db, go, 2)
@@ -1598,7 +1599,7 @@ class LoadOnExistingTest(_fixtures.FixtureTest):
         a2 = u1.addresses[0]
         a2.email_address = "foo"
         sess.query(User).options(
-            subqueryload("addresses").subqueryload("dingaling")
+            subqueryload(User.addresses).subqueryload(Address.dingaling)
         ).filter_by(id=8).all()
         assert u1.addresses[-1] is a1
         for a in u1.addresses:
@@ -1617,7 +1618,7 @@ class LoadOnExistingTest(_fixtures.FixtureTest):
         o1 = Order()
         u1.orders.append(o1)
         sess.query(User).options(
-            subqueryload("orders").subqueryload("items")
+            subqueryload(User.orders).subqueryload(Order.items)
         ).filter_by(id=7).all()
         for o in u1.orders:
             if o is not o1:
@@ -1631,11 +1632,11 @@ class LoadOnExistingTest(_fixtures.FixtureTest):
         u1 = (
             sess.query(User)
             .filter_by(id=8)
-            .options(subqueryload("addresses"))
+            .options(subqueryload(User.addresses))
             .one()
         )
         sess.query(User).filter_by(id=8).options(
-            subqueryload("addresses").subqueryload("dingaling")
+            subqueryload(User.addresses).subqueryload(Address.dingaling)
         ).first()
         assert "dingaling" in u1.addresses[0].__dict__
 
@@ -1645,11 +1646,11 @@ class LoadOnExistingTest(_fixtures.FixtureTest):
         u1 = (
             sess.query(User)
             .filter_by(id=7)
-            .options(subqueryload("orders"))
+            .options(subqueryload(User.orders))
             .one()
         )
         sess.query(User).filter_by(id=7).options(
-            subqueryload("orders").subqueryload("items")
+            subqueryload(User.orders).subqueryload(Order.items)
         ).first()
         assert "items" in u1.orders[0].__dict__
 
@@ -2536,7 +2537,7 @@ class SelfReferentialTest(fixtures.MappedTest):
             eq_(
                 Node(data="n1", children=[Node(data="n11"), Node(data="n12")]),
                 sess.query(Node)
-                .options(undefer("data"))
+                .options(undefer(Node.data))
                 .order_by(Node.id)
                 .first(),
             )
@@ -2549,7 +2550,10 @@ class SelfReferentialTest(fixtures.MappedTest):
             eq_(
                 Node(data="n1", children=[Node(data="n11"), Node(data="n12")]),
                 sess.query(Node)
-                .options(undefer("data"), undefer("children.data"))
+                .options(
+                    undefer(Node.data),
+                    defaultload(Node.children).undefer(Node.data),
+                )
                 .first(),
             )
 
@@ -2584,7 +2588,9 @@ class SelfReferentialTest(fixtures.MappedTest):
                 sess.query(Node)
                 .filter_by(data="n1")
                 .order_by(Node.id)
-                .options(subqueryload("children").subqueryload("children"))
+                .options(
+                    subqueryload(Node.children).subqueryload(Node.children)
+                )
                 .first()
             )
             eq_(
@@ -3189,7 +3195,9 @@ class JoinedNoLoadConflictTest(fixtures.DeclarativeMappedTest):
         # Parent->subqueryload->Child->joinedload->parent->noload->children.
         # the actual subqueryload has to emit *after* we've started populating
         # Parent->subqueryload->child.
-        parent = s.query(Parent).options([subqueryload("children")]).first()
+        parent = (
+            s.query(Parent).options([subqueryload(Parent.children)]).first()
+        )
         eq_(parent.children, [Child(name="c1")])
 
 
index afba9d95324271fd74928df72a39549e48c03f36..d95565cd403d1e3dd188f3932122b89b364f520e 100644 (file)
@@ -234,7 +234,7 @@ class UnicodeSchemaTest(fixtures.MappedTest):
 
         new_a1 = (
             session.query(A)
-            .options(sa.orm.joinedload("t2s"))
+            .options(sa.orm.joinedload(A.t2s))
             .filter(t1.c.a == a1.a)
         ).one()
         assert new_a1.a == a1.a