Check for the endmost target when chaining contains()

Fixed regression in association proxy due to 🎫`3769`
(allow for chained any() / has()) where contains() against
an association proxy chained in the form
(o2m relationship, associationproxy(m2o relationship, m2o relationship))
would raise an error regarding the re-application of contains()
on the final link of the chain.

Change-Id: Iea51ce84c2c5a332416fff10b1ba0e676cf0bad7
Fixes: #4150
This commit is contained in:
Mike Bayer
2018-01-02 17:56:45 -05:00
parent 435aaff20e
commit dcf66590d2
3 changed files with 53 additions and 1 deletions
+10
View File
@@ -0,0 +1,10 @@
.. change::
:tags: bug, ext
:tickets: 4150
Fixed regression in association proxy due to :ticket:`3769`
(allow for chained any() / has()) where contains() against
an association proxy chained in the form
(o2m relationship, associationproxy(m2o relationship, m2o relationship))
would raise an error regarding the re-application of contains()
on the final link of the chain.
+1
View File
@@ -462,6 +462,7 @@ class AssociationProxy(interfaces.InspectionAttrInfo):
if target_assoc is not None:
return self._comparator._criterion_exists(
target_assoc.contains(obj)
if not target_assoc.scalar else target_assoc == obj
)
elif self._target_is_object and self.scalar and \
not self._value_is_scalar:
+42 -1
View File
@@ -1281,6 +1281,10 @@ class ComparatorTest(fixtures.MappedTest, AssertsCompiledSQL):
# nonuselist -> nonuselist
user = association_proxy('user_keyword', 'user')
# uselist assoc_proxy -> collection -> assoc_proxy -> scalar object
# (o2m relationship, associationproxy(m2o relationship, m2o relationship))
singulars = association_proxy("user_keywords", "singular")
class UserKeyword(cls.Comparable):
def __init__(self, user=None, keyword=None):
self.user = user
@@ -1289,6 +1293,8 @@ class ComparatorTest(fixtures.MappedTest, AssertsCompiledSQL):
common_users = association_proxy("keyword", "user")
keyword_name = association_proxy("keyword", "keyword")
singular = association_proxy("user", "singular")
class Singular(cls.Comparable):
def __init__(self, value=None):
self.value = value
@@ -1311,7 +1317,8 @@ class ComparatorTest(fixtures.MappedTest, AssertsCompiledSQL):
'singular': relationship(Singular)
})
mapper(Keyword, keywords, properties={
'user_keyword': relationship(UserKeyword, uselist=False)
'user_keyword': relationship(UserKeyword, uselist=False),
'user_keywords': relationship(UserKeyword)
})
mapper(UserKeyword, userkeywords, properties={
@@ -1707,6 +1714,40 @@ class ComparatorTest(fixtures.MappedTest, AssertsCompiledSQL):
)
self._equivalent(q1, q2)
def test_filter_contains_chained_any_to_has_to_eq(self):
User = self.classes.User
Keyword = self.classes.Keyword
UserKeyword = self.classes.UserKeyword
Singular = self.classes.Singular
singular = self.session.query(Singular).order_by(Singular.id).first()
q1 = self.session.query(Keyword).filter(
Keyword.singulars.contains(singular)
)
self.assert_compile(
q1,
"SELECT keywords.id AS keywords_id, "
"keywords.keyword AS keywords_keyword, "
"keywords.singular_id AS keywords_singular_id "
"FROM keywords "
"WHERE EXISTS (SELECT 1 "
"FROM userkeywords "
"WHERE keywords.id = userkeywords.keyword_id AND "
"(EXISTS (SELECT 1 "
"FROM users "
"WHERE users.id = userkeywords.user_id AND "
":param_1 = users.singular_id)))",
checkparams={"param_1": singular.id}
)
q2 = self.session.query(Keyword).filter(
Keyword.user_keywords.any(
UserKeyword.user.has(User.singular == singular)
)
)
self._equivalent(q1, q2)
def test_has_criterion_nul(self):
# but we don't allow that with any criterion...
User = self.classes.User