Skip to content

Commit c7f0cd4

Browse files
committed
Fix filter() stopping one short in some situations.
1 parent 1f7c96d commit c7f0cd4

3 files changed

Lines changed: 23 additions & 2 deletions

File tree

peewee.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8750,7 +8750,8 @@ def convert_dict_to_node(self, qdict):
87508750
# direct FK lookup is all that is required.
87518751
model_attr = getattr(curr, key)
87528752
else:
8753-
for piece in key.split('__'):
8753+
pieces = key.split('__')
8754+
for i, piece in enumerate(pieces):
87548755
try:
87558756
model_attr = getattr(curr, piece, None)
87568757
except Exception:
@@ -8762,7 +8763,9 @@ def convert_dict_to_node(self, qdict):
87628763
break
87638764
else:
87648765
model_attr = getattr(curr, piece)
8765-
if value is not None and isinstance(model_attr, fks):
8766+
# Follow an fk only when a piece remains, the last
8767+
# one compares the fk column itself.
8768+
if i < len(pieces) - 1 and isinstance(model_attr, fks):
87668769
curr = model_attr.rel_model
87678770
joins.append(model_attr)
87688771
accum.append(op(model_attr, value))

tests/model_sql.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,22 @@ def test_filter_join(self):
406406
'INNER JOIN "users" AS "ua" ON ("t1"."user_id" = "ua"."id") '
407407
'WHERE ("ua"."username" = ?)'), ['huey'])
408408

409+
# A path ending in None still walks to the last model.
410+
query = Tweet.select(Tweet.content).filter(user__username=None)
411+
self.assertSQL(query, (
412+
'SELECT "t1"."content" FROM "tweet" AS "t1" '
413+
'INNER JOIN "users" AS "t2" ON ("t1"."user_id" = "t2"."id") '
414+
'WHERE ("t2"."username" IS NULL)'), [])
415+
416+
# A path ending on a foreign key compares the column and does not
417+
# join the related model.
418+
for value, clause, params in ((None, 'IS NULL', []), (3, '= ?', [3])):
419+
query = Favorite.select(Favorite.id).filter(tweet__user=value)
420+
self.assertSQL(query, (
421+
'SELECT "t1"."id" FROM "favorite" AS "t1" '
422+
'INNER JOIN "tweet" AS "t2" ON ("t1"."tweet_id" = "t2"."id") '
423+
'WHERE ("t2"."user_id" %s)' % clause), params)
424+
409425
def test_filter_with_or_across_joins(self):
410426
query = (Tweet
411427
.select(Tweet.content)

tests/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7630,6 +7630,8 @@ def assertNames(q, expected):
76307630

76317631
assertNames(DFGC.filter(dfc__df=a), ['a1-1', 'a1-2', 'a2-1'])
76327632
assertNames(DFGC.filter(dfc__df=a.id), ['a1-1', 'a1-2', 'a2-1'])
7633+
assertNames(DFGC.filter(dfc__df=None), [])
7634+
assertNames(DFGC.filter(dfc__df__name=None), [])
76337635

76347636
q = DFC.select().join(DF)
76357637
assertNames(q.filter(df=a), ['a1', 'a2'])

0 commit comments

Comments
 (0)