From: Mike Bayer Date: Tue, 28 Apr 2015 23:20:01 +0000 (-0400) Subject: - Fixed bug in association proxy where an any()/has() X-Git-Tag: rel_1_0_3~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4f6e9ccae93b9c50298b041356953cb8a96b4895;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Fixed bug in association proxy where an any()/has() on an relationship->scalar non-object attribute comparison would fail, e.g. ``filter(Parent.some_collection_to_attribute.any(Child.attr == 'foo'))`` fixes #3397 --- diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst index 5c39566b90..fd791ea979 100644 --- a/doc/build/changelog/changelog_10.rst +++ b/doc/build/changelog/changelog_10.rst @@ -18,6 +18,15 @@ .. changelog:: :version: 1.0.3 + .. change:: + :tags: bug, ext + :tickets: 3397 + + Fixed bug in association proxy where an any()/has() + on an relationship->scalar non-object attribute comparison would fail, + e.g. + ``filter(Parent.some_collection_to_attribute.any(Child.attr == 'foo'))`` + .. change:: :tags: bug, sql :tickets: 3396 diff --git a/lib/sqlalchemy/ext/associationproxy.py b/lib/sqlalchemy/ext/associationproxy.py index a741419735..d837aab520 100644 --- a/lib/sqlalchemy/ext/associationproxy.py +++ b/lib/sqlalchemy/ext/associationproxy.py @@ -365,13 +365,17 @@ class AssociationProxy(interfaces.InspectionAttrInfo): operators of the underlying proxied attributes. """ - - if self._value_is_scalar: - value_expr = getattr( - self.target_class, self.value_attr).has(criterion, **kwargs) + if self._target_is_object: + if self._value_is_scalar: + value_expr = getattr( + self.target_class, self.value_attr).has( + criterion, **kwargs) + else: + value_expr = getattr( + self.target_class, self.value_attr).any( + criterion, **kwargs) else: - value_expr = getattr( - self.target_class, self.value_attr).any(criterion, **kwargs) + value_expr = criterion # check _value_is_scalar here, otherwise # we're scalar->scalar - call .any() so that diff --git a/test/ext/test_associationproxy.py b/test/ext/test_associationproxy.py index 34c1a83220..8fb335b064 100644 --- a/test/ext/test_associationproxy.py +++ b/test/ext/test_associationproxy.py @@ -1089,7 +1089,8 @@ class ComparatorTest(fixtures.MappedTest, AssertsCompiledSQL): def define_tables(cls, metadata): Table('userkeywords', metadata, Column('keyword_id', Integer, ForeignKey('keywords.id'), primary_key=True), - Column('user_id', Integer, ForeignKey('users.id')) + Column('user_id', Integer, ForeignKey('users.id')), + Column('value', String(50)) ) Table('users', metadata, Column('id', Integer, @@ -1128,6 +1129,9 @@ class ComparatorTest(fixtures.MappedTest, AssertsCompiledSQL): # nonuselist singular_value = association_proxy('singular', 'value') + # o2m -> scalar + singular_collection = association_proxy('user_keywords', 'value') + class Keyword(cls.Comparable): def __init__(self, keyword): self.keyword = keyword @@ -1195,8 +1199,9 @@ class ComparatorTest(fixtures.MappedTest, AssertsCompiledSQL): for jj in words[(ii % len(words)):((ii + 3) % len(words))]: k = Keyword(jj) user.keywords.append(k) - if ii % 3 == None: + if ii % 2 == 0: user.singular.keywords.append(k) + user.user_keywords[-1].value = "singular%d" % ii orphan = Keyword('orphan') orphan.user_keyword = UserKeyword(keyword=orphan, user=None) @@ -1213,6 +1218,27 @@ class ComparatorTest(fixtures.MappedTest, AssertsCompiledSQL): def _equivalent(self, q_proxy, q_direct): eq_(q_proxy.all(), q_direct.all()) + def test_filter_any_criterion_ul_scalar(self): + UserKeyword, User = self.classes.UserKeyword, self.classes.User + + q1 = self.session.query(User).filter( + User.singular_collection.any(UserKeyword.value == 'singular8')) + self.assert_compile( + q1, + "SELECT users.id AS users_id, users.name AS users_name, " + "users.singular_id AS users_singular_id " + "FROM users " + "WHERE EXISTS (SELECT 1 " + "FROM userkeywords " + "WHERE users.id = userkeywords.user_id AND " + "userkeywords.value = :value_1)", + checkparams={'value_1': 'singular8'} + ) + + q2 = self.session.query(User).filter( + User.user_keywords.any(UserKeyword.value == 'singular8')) + self._equivalent(q1, q2) + def test_filter_any_kwarg_ul_nul(self): UserKeyword, User = self.classes.UserKeyword, self.classes.User