diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-08-30 15:45:50 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-08-30 15:45:50 -0400 |
commit | f995a63d6c0b5dd072cbadee2bf78b233f454061 (patch) | |
tree | f6ec929636458e8fd24011849732118110a6f6af | |
parent | 0bd074cc5ae9fed32e6f7d98d687aba3c9dd52c2 (diff) | |
download | sqlalchemy-f995a63d6c0b5dd072cbadee2bf78b233f454061.tar.gz |
- alter the yield_per eager restriction such that joined many-to-one loads
are still OK, since these should be fine.
-rw-r--r-- | doc/build/changelog/changelog_10.rst | 9 | ||||
-rw-r--r-- | doc/build/changelog/migration_10.rst | 20 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/query.py | 19 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/strategies.py | 4 | ||||
-rw-r--r-- | test/orm/test_query.py | 68 |
5 files changed, 100 insertions, 20 deletions
diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst index 016be974b..5f12cc969 100644 --- a/doc/build/changelog/changelog_10.rst +++ b/doc/build/changelog/changelog_10.rst @@ -25,11 +25,12 @@ :tags: feature, orm The :class:`.Query` will raise an exception when :meth:`.Query.yield_per` - is used with mappings or options where eager loading, either - joined or subquery, would take place. These loading strategies are + is used with mappings or options where either + subquery eager loading, or joined eager loading with collections, + would take place. These loading strategies are not currently compatible with yield_per, so by raising this error, - the method is safer to use - combine with sending False to - :meth:`.Query.enable_eagerloads` to disable the eager loaders. + the method is safer to use. Eager loads can be disabled with + the ``lazyload('*')`` option or :meth:`.Query.enable_eagerloads`. .. seealso:: diff --git a/doc/build/changelog/migration_10.rst b/doc/build/changelog/migration_10.rst index 1aa0129c3..533682ebc 100644 --- a/doc/build/changelog/migration_10.rst +++ b/doc/build/changelog/migration_10.rst @@ -110,11 +110,25 @@ Joined/Subquery eager loading explicitly disallowed with yield_per ------------------------------------------------------------------ In order to make the :meth:`.Query.yield_per` method easier to use, -an exception is raised if any joined or subquery eager loaders are +an exception is raised if any subquery eager loaders, or joined +eager loaders that would use collections, are to take effect when yield_per is used, as these are currently not compatible with yield-per (subquery loading could be in theory, however). -When this error is raised, the :meth:`.Query.enable_eagerloads` method -should be called with a value of False to disable these eager loaders. +When this error is raised, the :func:`.lazyload` option can be sent with +an asterisk:: + + q = sess.query(Object).options(lazyload('*')).yield_per(100) + +or use :meth:`.Query.enable_eagerloads`:: + + q = sess.query(Object).enable_eagerloads(False).yield_per(100) + +The :func:`.lazyload` option has the advantage that additional many-to-one +joined loader options can still be used:: + + q = sess.query(Object).options( + lazyload('*'), joinedload("some_manytoone")).yield_per(100) + .. _migration_migration_deprecated_orm_events: diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 372eba0fe..72989f34b 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -604,7 +604,8 @@ class Query(object): raise sa_exc.InvalidRequestError( "The yield_per Query option is currently not " "compatible with %s eager loading. Please " - "specify query.enable_eagerloads(False) in order to " + "specify lazyload('*') or query.enable_eagerloads(False) in " + "order to " "proceed with query.yield_per()." % message) @_generative() @@ -722,10 +723,18 @@ class Query(object): rows (which are most). The :meth:`.Query.yield_per` method **is not compatible with most - eager loading schemes, including joinedload and subqueryload**. - For this reason it typically should be combined with the use of - the :meth:`.Query.enable_eagerloads` method, passing a value of - False. See the warning below. + eager loading schemes, including subqueryload and joinedload with + collections**. For this reason, it may be helpful to disable + eager loads, either unconditionally with + :meth:`.Query.enable_eagerloads`:: + + q = sess.query(Object).yield_per(100).enable_eagerloads(False) + + Or more selectively using :func:`.lazyload`; such as with + an asterisk to specify the default loader scheme:: + + q = sess.query(Object).yield_per(100).\\ + options(lazyload('*'), joinedload(Object.some_related)) .. warning:: diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index e31b3ae6d..2159d9135 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -1086,8 +1086,8 @@ class JoinedLoader(AbstractRelationshipLoader): if not context.query._enable_eagerloads: return - elif context.query._yield_per: - context.query._no_yield_per("joined") + elif context.query._yield_per and self.uselist: + context.query._no_yield_per("joined collection") path = path[self.parent_property] diff --git a/test/orm/test_query.py b/test/orm/test_query.py index 6ac1ba5af..8f83b07aa 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -7,7 +7,7 @@ from sqlalchemy.engine import default from sqlalchemy.orm import ( attributes, mapper, relationship, create_session, synonym, Session, aliased, column_property, joinedload_all, joinedload, Query, Bundle, - subqueryload) + subqueryload, backref, lazyload) from sqlalchemy.testing.assertsql import CompiledSQL from sqlalchemy.testing.schema import Table, Column import sqlalchemy as sa @@ -2116,8 +2116,24 @@ class PrefixWithTest(QueryTest, AssertsCompiledSQL): self.assert_compile(query, expected, dialect=default.DefaultDialect()) -class YieldTest(QueryTest): +class YieldTest(_fixtures.FixtureTest): + run_setup_mappers = 'each' + run_inserts = 'each' + + def _eagerload_mappings(self, addresses_lazy=True, user_lazy=True): + User, Address = self.classes("User", "Address") + users, addresses = self.tables("users", "addresses") + mapper(User, users, properties={ + "addresses": relationship( + Address, lazy=addresses_lazy, + backref=backref("user", lazy=user_lazy) + ) + }) + mapper(Address, addresses) + def test_basic(self): + self._eagerload_mappings() + User = self.classes.User sess = create_session() @@ -2140,6 +2156,8 @@ class YieldTest(QueryTest): pass def test_yield_per_and_execution_options(self): + self._eagerload_mappings() + User = self.classes.User sess = create_session() @@ -2148,18 +2166,22 @@ class YieldTest(QueryTest): assert q._yield_per eq_(q._execution_options, {"stream_results": True, "foo": "bar"}) - def test_no_joinedload(self): + def test_no_joinedload_opt(self): + self._eagerload_mappings() + User = self.classes.User sess = create_session() q = sess.query(User).options(joinedload("addresses")).yield_per(1) assert_raises_message( sa_exc.InvalidRequestError, "The yield_per Query option is currently not compatible with " - "joined eager loading. Please specify ", + "joined collection eager loading. Please specify ", q.all ) - def test_no_subqueryload(self): + def test_no_subqueryload_opt(self): + self._eagerload_mappings() + User = self.classes.User sess = create_session() q = sess.query(User).options(subqueryload("addresses")).yield_per(1) @@ -2170,7 +2192,29 @@ class YieldTest(QueryTest): q.all ) - def test_eagerload_disable(self): + def test_no_subqueryload_mapping(self): + self._eagerload_mappings(addresses_lazy="subquery") + + User = self.classes.User + sess = create_session() + q = sess.query(User).yield_per(1) + assert_raises_message( + sa_exc.InvalidRequestError, + "The yield_per Query option is currently not compatible with " + "subquery eager loading. Please specify ", + q.all + ) + + def test_joinedload_m2o_ok(self): + self._eagerload_mappings(user_lazy="joined") + Address = self.classes.Address + sess = create_session() + q = sess.query(Address).yield_per(1) + q.all() + + def test_eagerload_opt_disable(self): + self._eagerload_mappings() + User = self.classes.User sess = create_session() q = sess.query(User).options(subqueryload("addresses")).\ @@ -2181,6 +2225,18 @@ class YieldTest(QueryTest): enable_eagerloads(False).yield_per(1) q.all() + def test_m2o_joinedload_not_others(self): + self._eagerload_mappings(addresses_lazy="joined") + Address = self.classes.Address + sess = create_session() + q = sess.query(Address).options( + lazyload('*'), joinedload("user")).yield_per(1).filter_by(id=1) + + def go(): + result = q.all() + assert result[0].user + self.assert_sql_count(testing.db, go, 1) + class HintsTest(QueryTest, AssertsCompiledSQL): def test_hints(self): |