From f20f45fae24a4818dc8be5d31a46695b9d16c897 Mon Sep 17 00:00:00 2001 From: Matthew Martin Date: Sun, 11 Jun 2023 10:00:22 -0400 Subject: [PATCH] Add `_WILDCARD_TOKEN` special case in `_chop_path` ### Description Add special case to `_chop_path` for initial `_WILDCARD_TOKEN` matching `_DEFAULT_TOKEN` to allow for a top level `undefer_group` load option with other load options. Fixes #9870. ### Checklist This pull request is: - [ ] A documentation / typographical error fix - Good to go, no issue or tests are needed - [x] A short code fix - please include the issue number, and create an issue if none exists, which must include a complete example of the issue. one line code fixes without an issue and demonstration will not be accepted. - Please include: `Fixes: #` in the commit message - please include tests. one line code fixes without tests will not be accepted. - [ ] A new feature implementation - please include the issue number, and create an issue if none exists, which must include a complete example of how the feature would look. - Please include: `Fixes: #` in the commit message - please include tests. **Have a nice day!** Closes: #9931 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/9931 Pull-request-sha: c073f0904f12909ff660beef6576f6476a725592 Change-Id: Ic1158b3b0306e9a1bba86ab0b9f07700a6352398 --- doc/build/changelog/unreleased_20/9870.rst | 8 +++ lib/sqlalchemy/orm/strategy_options.py | 5 +- test/orm/test_deferred.py | 69 ++++++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 doc/build/changelog/unreleased_20/9870.rst diff --git a/doc/build/changelog/unreleased_20/9870.rst b/doc/build/changelog/unreleased_20/9870.rst new file mode 100644 index 0000000000..3c3a5dd0a1 --- /dev/null +++ b/doc/build/changelog/unreleased_20/9870.rst @@ -0,0 +1,8 @@ +.. change:: + :tags: bug, orm, regression + :tickets: 9870 + + Fixed regression in the 2.0 series where a query that used + :func:`.undefer_group` with :func:`_orm.selectinload` or + :func:`_orm.subqueryload` would raise an ``AttributeError``. Pull request + courtesy of Matthew Martin. diff --git a/lib/sqlalchemy/orm/strategy_options.py b/lib/sqlalchemy/orm/strategy_options.py index 90b8dda8b0..d93ec0a273 100644 --- a/lib/sqlalchemy/orm/strategy_options.py +++ b/lib/sqlalchemy/orm/strategy_options.py @@ -931,7 +931,10 @@ class _AbstractLoad(traversals.GenerativeOnTraversal, LoaderOption): zip(to_chop, path.natural_path) ): if isinstance(c_token, str): - if i == 0 and c_token.endswith(f":{_DEFAULT_TOKEN}"): + if i == 0 and ( + c_token.endswith(f":{_DEFAULT_TOKEN}") + or c_token.endswith(f":{_WILDCARD_TOKEN}") + ): return to_chop elif ( c_token != f"{_RELATIONSHIP_TOKEN}:{_WILDCARD_TOKEN}" diff --git a/test/orm/test_deferred.py b/test/orm/test_deferred.py index 7435299240..c93ac6d60a 100644 --- a/test/orm/test_deferred.py +++ b/test/orm/test_deferred.py @@ -692,6 +692,75 @@ class DeferredOptionsTest(AssertsCompiledSQL, _fixtures.FixtureTest): ], ) + def test_undefer_group_with_load(self): + users, Order, User, orders = ( + self.tables.users, + self.classes.Order, + self.classes.User, + self.tables.orders, + ) + + self.mapper_registry.map_imperatively( + User, + users, + ) + self.mapper_registry.map_imperatively( + Order, + orders, + properties=util.OrderedDict( + [ + ("userident", deferred(orders.c.user_id, group="primary")), + ( + "description", + deferred(orders.c.description, group="primary"), + ), + ("opened", deferred(orders.c.isopen, group="primary")), + ("user", relationship(User)), + ] + ), + ) + + sess = fixture_session() + q = ( + sess.query(Order) + .filter(Order.id == 3) + .options( + selectinload(Order.user), + undefer_group("primary"), + ) + ) + + def go(): + result = q.all() + print(result) + o = result[0] + eq_(o.opened, 1) + eq_(o.userident, 7) + eq_(o.description, "order 3") + u = o.user + eq_(u.id, 7) + + self.sql_eq_( + go, + [ + ( + "SELECT orders.id AS orders_id, " + "orders.user_id AS orders_user_id, " + "orders.address_id AS orders_address_id, " + "orders.description AS orders_description, " + "orders.isopen AS orders_isopen " + "FROM orders WHERE orders.id = :id_1", + {"id_1": 3}, + ), + ( + "SELECT users.id AS users_id, users.name AS users_name " + "FROM users WHERE users.id IN " + "(__[POSTCOMPILE_primary_keys])", + [{"primary_keys": [7]}], + ), + ], + ) + def test_undefer_group_from_relationship_lazyload(self): users, Order, User, orders = ( self.tables.users, -- 2.39.5