From: Patrick Hayes Date: Thu, 24 Oct 2019 14:11:52 +0000 (-0400) Subject: Add public accessor `is_single_entity` to Query X-Git-Tag: rel_1_3_11~20 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5f38875bb4c3f9cafa86c87f83219e32e45f5791;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Add public accessor `is_single_entity` to Query Added accessor :attr:`.Query.is_single_entity` to :class:`.Query`, which will indicate if the results returned by this :class:`.Query` will be a list of ORM entities, or a tuple of entities or column expressions. SQLAlchemy hopes to improve upon the behavior of single entity / tuples in future releases such that the behavior would be explicit up front, however this attribute should be helpful with the current behavior. Pull request courtesy Patrick Hayes. Fixes: #4934 Closes: #4935 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/4935 Pull-request-sha: 98f72b40a896761a65b048cc3722ff2dac4cf2b1 Change-Id: If5db5db3ea46a93406d76ef90b5b588149ba2986 (cherry picked from commit e55e749e182e257fcc60db383751d5afbe52ddfb) --- diff --git a/doc/build/changelog/unreleased_13/4934.rst b/doc/build/changelog/unreleased_13/4934.rst new file mode 100644 index 0000000000..3f279a4308 --- /dev/null +++ b/doc/build/changelog/unreleased_13/4934.rst @@ -0,0 +1,11 @@ +.. change:: + :tags: orm, usecase + :tickets: 4934 + + Added accessor :meth:`.Query.is_single_entity` to :class:`.Query`, which + will indicate if the results returned by this :class:`.Query` will be a + list of ORM entities, or a tuple of entities or column expressions. + SQLAlchemy hopes to improve upon the behavior of single entity / tuples in + future releases such that the behavior would be explicit up front, however + this attribute should be helpful with the current behavior. Pull request + courtesy Patrick Hayes. diff --git a/lib/sqlalchemy/orm/loading.py b/lib/sqlalchemy/orm/loading.py index fd283f4327..78ad2eb3b9 100644 --- a/lib/sqlalchemy/orm/loading.py +++ b/lib/sqlalchemy/orm/loading.py @@ -40,11 +40,7 @@ def instances(query, cursor, context): filtered = query._has_mapper_entities - single_entity = ( - not query._only_return_tuples - and len(query._entities) == 1 - and query._entities[0].supports_single_entity - ) + single_entity = query.is_single_entity if filtered: if single_entity: diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index f4cf34458c..d3629690ed 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -660,14 +660,40 @@ class Query(object): @_generative() def only_return_tuples(self, value): - """When set to True, the query results will always be a tuple, - specifically for single element queries. The default is False. + """When set to True, the query results will always be a tuple. - . .. versionadded:: 1.2.5 + This is specifically for single element queries. The default is False. + + .. versionadded:: 1.2.5 + + .. seealso:: + + :meth:`.Query.is_single_entity` """ self._only_return_tuples = value + @property + def is_single_entity(self): + """Indicates if this :class:`.Query` returns tuples or single entities. + + Returns True if this query returns a single entity for each instance + in its result list, and False if this query returns a tuple of entities + for each result. + + .. versionadded:: 1.3.11 + + .. seealso:: + + :meth:`.Query.only_return_tuples` + + """ + return ( + not self._only_return_tuples + and len(self._entities) == 1 + and self._entities[0].supports_single_entity + ) + @_generative() def enable_eagerloads(self, value): """Control whether or not eager joins and subqueries are diff --git a/test/orm/test_query.py b/test/orm/test_query.py index 9ba5ed3b5b..3cab1169f5 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -53,7 +53,9 @@ from sqlalchemy.sql import operators from sqlalchemy.testing import AssertsCompiledSQL from sqlalchemy.testing import fixtures from sqlalchemy.testing import is_ +from sqlalchemy.testing import is_false from sqlalchemy.testing import is_not_ +from sqlalchemy.testing import is_true from sqlalchemy.testing import mock from sqlalchemy.testing.assertions import assert_raises from sqlalchemy.testing.assertions import assert_raises_message @@ -93,32 +95,30 @@ class MiscTest(QueryTest): class OnlyReturnTuplesTest(QueryTest): def test_single_entity_false(self): User = self.classes.User - row = create_session().query(User).only_return_tuples(False).first() + query = create_session().query(User).only_return_tuples(False) + is_true(query.is_single_entity) + row = query.first() assert isinstance(row, User) def test_single_entity_true(self): User = self.classes.User - row = create_session().query(User).only_return_tuples(True).first() + query = create_session().query(User).only_return_tuples(True) + is_false(query.is_single_entity) + row = query.first() assert isinstance(row, tuple) def test_multiple_entity_false(self): User = self.classes.User - row = ( - create_session() - .query(User.id, User) - .only_return_tuples(False) - .first() - ) + query = create_session().query(User.id, User).only_return_tuples(False) + is_false(query.is_single_entity) + row = query.first() assert isinstance(row, tuple) def test_multiple_entity_true(self): User = self.classes.User - row = ( - create_session() - .query(User.id, User) - .only_return_tuples(True) - .first() - ) + query = create_session().query(User.id, User).only_return_tuples(True) + is_false(query.is_single_entity) + row = query.first() assert isinstance(row, tuple)