diff options
35 files changed, 1792 insertions, 840 deletions
diff --git a/lib/sqlalchemy/ext/associationproxy.py b/lib/sqlalchemy/ext/associationproxy.py index 411033a6b..dd5c10ac9 100644 --- a/lib/sqlalchemy/ext/associationproxy.py +++ b/lib/sqlalchemy/ext/associationproxy.py @@ -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:: diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 1aa6666a5..4de12b88c 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -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 = [ diff --git a/lib/sqlalchemy/orm/strategy_options.py b/lib/sqlalchemy/orm/strategy_options.py index b7ed4e89b..675c7218b 100644 --- a/lib/sqlalchemy/orm/strategy_options.py +++ b/lib/sqlalchemy/orm/strategy_options.py @@ -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 diff --git a/lib/sqlalchemy/testing/warnings.py b/lib/sqlalchemy/testing/warnings.py index 9e02a0c03..9886b122d 100644 --- a/lib/sqlalchemy/testing/warnings.py +++ b/lib/sqlalchemy/testing/warnings.py @@ -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 # @@ -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] diff --git a/test/ext/test_associationproxy.py b/test/ext/test_associationproxy.py index bbb430a6f..258ecb90c 100644 --- a/test/ext/test_associationproxy.py +++ b/test/ext/test_associationproxy.py @@ -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 = " diff --git a/test/ext/test_horizontal_shard.py b/test/ext/test_horizontal_shard.py index a0bc8bbe5..192617901 100644 --- a/test/ext/test_horizontal_shard.py +++ b/test/ext/test_horizontal_shard.py @@ -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]) diff --git a/test/orm/inheritance/test_assorted_poly.py b/test/orm/inheritance/test_assorted_poly.py index 2db4641dd..729e1ee04 100644 --- a/test/orm/inheritance/test_assorted_poly.py +++ b/test/orm/inheritance/test_assorted_poly.py @@ -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)]) diff --git a/test/orm/inheritance/test_basic.py b/test/orm/inheritance/test_basic.py index 52b362b9e..e870a80f0 100644 --- a/test/orm/inheritance/test_basic.py +++ b/test/orm/inheritance/test_basic.py @@ -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() ) diff --git a/test/orm/inheritance/test_polymorphic_rel.py b/test/orm/inheritance/test_polymorphic_rel.py index 1a96ee011..b9d9ad932 100644 --- a/test/orm/inheritance/test_polymorphic_rel.py +++ b/test/orm/inheritance/test_polymorphic_rel.py @@ -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%")) diff --git a/test/orm/inheritance/test_relationship.py b/test/orm/inheritance/test_relationship.py index e795f0c7f..881353e3b 100644 --- a/test/orm/inheritance/test_relationship.py +++ b/test/orm/inheritance/test_relationship.py @@ -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 " diff --git a/test/orm/inheritance/test_single.py b/test/orm/inheritance/test_single.py index a3230f91d..690bdfc57 100644 --- a/test/orm/inheritance/test_single.py +++ b/test/orm/inheritance/test_single.py @@ -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(), [ diff --git a/test/orm/test_ac_relationships.py b/test/orm/test_ac_relationships.py index 26dd67480..6a050b698 100644 --- a/test/orm/test_ac_relationships.py +++ b/test/orm/test_ac_relationships.py @@ -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 diff --git a/test/orm/test_assorted_eager.py b/test/orm/test_assorted_eager.py index 6f983e0ed..a97538da6 100644 --- a/test/orm/test_assorted_eager.py +++ b/test/orm/test_assorted_eager.py @@ -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() diff --git a/test/orm/test_cache_key.py b/test/orm/test_cache_key.py index f25a57fe5..d689bd6ba 100644 --- a/test/orm/test_cache_key.py +++ b/test/orm/test_cache_key.py @@ -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, diff --git a/test/orm/test_composites.py b/test/orm/test_composites.py index 4bdca7a45..1dc139df9 100644 --- a/test/orm/test_composites.py +++ b/test/orm/test_composites.py @@ -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_( diff --git a/test/orm/test_default_strategies.py b/test/orm/test_default_strategies.py index 249a446ea..9b228bbaa 100644 --- a/test/orm/test_default_strategies.py +++ b/test/orm/test_default_strategies.py @@ -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() ) diff --git a/test/orm/test_deferred.py b/test/orm/test_deferred.py index 9f9068783..e44d6ec86 100644 --- a/test/orm/test_deferred.py +++ b/test/orm/test_deferred.py @@ -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) diff --git a/test/orm/test_deprecations.py b/test/orm/test_deprecations.py index ddfad7e0f..2ab45827b 100644 --- a/test/orm/test_deprecations.py +++ b/test/orm/test_deprecations.py @@ -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) diff --git a/test/orm/test_eager_relations.py b/test/orm/test_eager_relations.py index cb578ee2d..c235edadf 100644 --- a/test/orm/test_eager_relations.py +++ b/test/orm/test_eager_relations.py @@ -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) diff --git a/test/orm/test_expire.py b/test/orm/test_expire.py index 92a7ea42d..d9204db96 100644 --- a/test/orm/test_expire.py +++ b/test/orm/test_expire.py @@ -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() diff --git a/test/orm/test_froms.py b/test/orm/test_froms.py index d6579cebd..3351967fc 100644 --- a/test/orm/test_froms.py +++ b/test/orm/test_froms.py @@ -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) diff --git a/test/orm/test_generative.py b/test/orm/test_generative.py index 1a40447a6..071346277 100644 --- a/test/orm/test_generative.py +++ b/test/orm/test_generative.py @@ -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) diff --git a/test/orm/test_joins.py b/test/orm/test_joins.py index eb7dfd6f3..42f9b9aa1 100644 --- a/test/orm/test_joins.py +++ b/test/orm/test_joins.py @@ -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": diff --git a/test/orm/test_lazy_relations.py b/test/orm/test_lazy_relations.py index 457cfee4e..2bea9b110 100644 --- a/test/orm/test_lazy_relations.py +++ b/test/orm/test_lazy_relations.py @@ -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): diff --git a/test/orm/test_mapper.py b/test/orm/test_mapper.py index 6c8b26c71..7d9e6f9c3 100644 --- a/test/orm/test_mapper.py +++ b/test/orm/test_mapper.py @@ -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) diff --git a/test/orm/test_merge.py b/test/orm/test_merge.py index 7d61a8722..3b97bd5a5 100644 --- a/test/orm/test_merge.py +++ b/test/orm/test_merge.py @@ -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 diff --git a/test/orm/test_of_type.py b/test/orm/test_of_type.py index bc32e322d..bdf7ab859 100644 --- a/test/orm/test_of_type.py +++ b/test/orm/test_of_type.py @@ -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], diff --git a/test/orm/test_options.py b/test/orm/test_options.py index ebbbe738d..1a2a5ba70 100644 --- a/test/orm/test_options.py +++ b/test/orm/test_options.py @@ -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]]) diff --git a/test/orm/test_pickled.py b/test/orm/test_pickled.py index e33ec9a5c..e6f15c47b 100644 --- a/test/orm/test_pickled.py +++ b/test/orm/test_pickled.py @@ -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 = [ diff --git a/test/orm/test_query.py b/test/orm/test_query.py index 3e58f211c..c9353f551 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -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) diff --git a/test/orm/test_relationships.py b/test/orm/test_relationships.py index a25e39c82..2547a981a 100644 --- a/test/orm/test_relationships.py +++ b/test/orm/test_relationships.py @@ -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() ) diff --git a/test/orm/test_selectin_relations.py b/test/orm/test_selectin_relations.py index ceaead9c4..e3d852e68 100644 --- a/test/orm/test_selectin_relations.py +++ b/test/orm/test_selectin_relations.py @@ -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_( diff --git a/test/orm/test_subquery_relations.py b/test/orm/test_subquery_relations.py index 70d8d19bd..25ebb9ebe 100644 --- a/test/orm/test_subquery_relations.py +++ b/test/orm/test_subquery_relations.py @@ -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")]) diff --git a/test/orm/test_unitofwork.py b/test/orm/test_unitofwork.py index afba9d953..d95565cd4 100644 --- a/test/orm/test_unitofwork.py +++ b/test/orm/test_unitofwork.py @@ -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 |
