From: Mike Bayer Date: Tue, 2 Jul 2024 17:57:47 +0000 (-0400) Subject: call iter() on detached/transient dynamic session X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d9d98eacca11490b7df878ef399b92fbb2df2f47;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git call iter() on detached/transient dynamic session Fixed regression going back to 1.4 where accessing a collection using the "dynamic" strategy on a transient object and attempting to query would raise an internal error rather than the expected :class:`.NoResultFound` that occurred in 1.3. Fixes: #11562 Change-Id: I650305963a17592413520d8d1049c601761a0acc --- diff --git a/doc/build/changelog/unreleased_14/11562.rst b/doc/build/changelog/unreleased_14/11562.rst new file mode 100644 index 0000000000..15ccd0df6d --- /dev/null +++ b/doc/build/changelog/unreleased_14/11562.rst @@ -0,0 +1,8 @@ +.. change:: + :tags: bug, orm, regression + :tickets: 11562 + + Fixed regression going back to 1.4 where accessing a collection using the + "dynamic" strategy on a transient object and attempting to query would + raise an internal error rather than the expected :class:`.NoResultFound` + that occurred in 1.3. diff --git a/lib/sqlalchemy/orm/dynamic.py b/lib/sqlalchemy/orm/dynamic.py index 7496e5c30d..ad1b239c13 100644 --- a/lib/sqlalchemy/orm/dynamic.py +++ b/lib/sqlalchemy/orm/dynamic.py @@ -161,10 +161,12 @@ class AppenderMixin(AbstractCollectionWriter[_T]): return result.IteratorResult( result.SimpleResultMetaData([self.attr.class_.__name__]), - self.attr._get_collection_history( # type: ignore[arg-type] - attributes.instance_state(self.instance), - PassiveFlag.PASSIVE_NO_INITIALIZE, - ).added_items, + iter( + self.attr._get_collection_history( + attributes.instance_state(self.instance), + PassiveFlag.PASSIVE_NO_INITIALIZE, + ).added_items + ), _source_supports_scalars=True, ).scalars() else: diff --git a/test/orm/test_dynamic.py b/test/orm/test_dynamic.py index cce3f8c18a..465e29929e 100644 --- a/test/orm/test_dynamic.py +++ b/test/orm/test_dynamic.py @@ -275,6 +275,33 @@ class DynamicTest(_DynamicFixture, _fixtures.FixtureTest, AssertsCompiledSQL): use_default_dialect=True, ) + @testing.combinations( + ("all", []), + ("one", exc.NoResultFound), + ("one_or_none", None), + argnames="method, expected", + ) + @testing.variation("add_to_session", [True, False]) + def test_transient_raise( + self, user_address_fixture, method, expected, add_to_session + ): + """test 11562""" + User, Address = user_address_fixture() + + u1 = User(name="u1") + if add_to_session: + sess = fixture_session() + sess.add(u1) + + meth = getattr(u1.addresses, method) + if expected is exc.NoResultFound: + with expect_raises_message( + exc.NoResultFound, "No row was found when one was required" + ): + meth() + else: + eq_(meth(), expected) + def test_detached_raise(self, user_address_fixture): """so filtering on a detached dynamic list raises an error..."""