From: Mike Bayer Date: Thu, 11 Jun 2015 20:48:00 +0000 (-0400) Subject: - Fixed an unexpected-use regression whereby custom :class:`.Comparator` X-Git-Tag: rel_1_0_6~26 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e765c55e8cc71bb3773b86b5260df6cb69aff102;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Fixed an unexpected-use regression whereby custom :class:`.Comparator` objects that made use of the ``__clause_element__()`` method and returned an object that was an ORM-mapped :class:`.InstrumentedAttribute` and not explicitly a :class:`.ColumnElement` would fail to be correctly handled when passed as an expression to :meth:`.Session.query`. The logic in 0.9 happened to succeed on this, so this use case is now supported. fixes #3448 --- diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst index 3436a0ca6a..f448865591 100644 --- a/doc/build/changelog/changelog_10.rst +++ b/doc/build/changelog/changelog_10.rst @@ -18,6 +18,19 @@ .. changelog:: :version: 1.0.6 + .. change:: + :tags: bug, orm + :tickets: 3448 + + Fixed an unexpected-use regression whereby custom :class:`.Comparator` + objects that made use of the ``__clause_element__()`` method and + returned an object that was an ORM-mapped + :class:`.InstrumentedAttribute` and not explicitly a + :class:`.ColumnElement` would fail to be correctly + handled when passed as an expression to :meth:`.Session.query`. + The logic in 0.9 happened to succeed on this, so this use case is now + supported. + .. change:: :tags: bug, sql :tickets: 3445 diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 8421e42acc..4f8c86a144 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -3539,11 +3539,13 @@ class _ColumnEntity(_QueryEntity): self.expr = column self.namespace = namespace search_entities = True + check_column = False if isinstance(column, util.string_types): column = sql.literal_column(column) self._label_name = column.name search_entities = False + check_column = True _entity = None elif isinstance(column, ( attributes.QueryableAttribute, @@ -3554,10 +3556,12 @@ class _ColumnEntity(_QueryEntity): search_entities = False self._label_name = column.key column = column._query_clause_element() + check_column = True if isinstance(column, Bundle): _BundleEntity(query, column) return - elif not isinstance(column, sql.ColumnElement): + + if not isinstance(column, sql.ColumnElement): if hasattr(column, '_select_iterable'): # break out an object like Table into # individual columns @@ -3572,7 +3576,7 @@ class _ColumnEntity(_QueryEntity): "SQL expression, column, or mapped entity " "expected - got '%r'" % (column, ) ) - else: + elif not check_column: self._label_name = getattr(column, 'key', None) search_entities = True diff --git a/test/ext/test_hybrid.py b/test/ext/test_hybrid.py index b895d2fb2b..e36b8f7e9e 100644 --- a/test/ext/test_hybrid.py +++ b/test/ext/test_hybrid.py @@ -7,6 +7,7 @@ from sqlalchemy.testing import eq_, AssertsCompiledSQL, assert_raises_message from sqlalchemy.testing import fixtures from sqlalchemy import inspect + class PropertyComparatorTest(fixtures.TestBase, AssertsCompiledSQL): __dialect__ = 'default' diff --git a/test/orm/test_descriptor.py b/test/orm/test_descriptor.py index 2134d87b29..d9aca30e55 100644 --- a/test/orm/test_descriptor.py +++ b/test/orm/test_descriptor.py @@ -125,3 +125,4 @@ class DescriptorInstrumentationTest(fixtures.ORMTest): str(aliased(Foo).foo == 'ed'), "foobar(foo_1.name) = foobar(:foobar_1)" ) + diff --git a/test/orm/test_query.py b/test/orm/test_query.py index 6a1eb57b48..41c0e2a210 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -1718,6 +1718,25 @@ class ColumnPropertyTest(_fixtures.FixtureTest, AssertsCompiledSQL): ) +class ComparatorTest(QueryTest): + def test_clause_element_query_resolve(self): + from sqlalchemy.orm.properties import ColumnProperty + User = self.classes.User + + class Comparator(ColumnProperty.Comparator): + def __init__(self, expr): + self.expr = expr + + def __clause_element__(self): + return self.expr + + sess = Session() + eq_( + sess.query(Comparator(User.id)).order_by(Comparator(User.id)).all(), + [(7, ), (8, ), (9, ), (10, )] + ) + + # more slice tests are available in test/orm/generative.py class SliceTest(QueryTest): def test_first(self):