summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2015-04-28 19:20:01 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2015-04-28 19:20:01 -0400
commit4f6e9ccae93b9c50298b041356953cb8a96b4895 (patch)
tree32b9814c8d7629ab593cd9bf8ada5fdef86522c8
parentac52239b328f6dc573fdfb9acbbc7d5d528fa982 (diff)
downloadsqlalchemy-4f6e9ccae93b9c50298b041356953cb8a96b4895.tar.gz
- 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
-rw-r--r--doc/build/changelog/changelog_10.rst9
-rw-r--r--lib/sqlalchemy/ext/associationproxy.py16
-rw-r--r--test/ext/test_associationproxy.py30
3 files changed, 47 insertions, 8 deletions
diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst
index 5c39566b9..fd791ea97 100644
--- a/doc/build/changelog/changelog_10.rst
+++ b/doc/build/changelog/changelog_10.rst
@@ -19,6 +19,15 @@
: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 a74141973..d837aab52 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 34c1a8322..8fb335b06 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