Fix exclude-only multi filters matching nothing
MultiCriterion.to_q (used by SessionFilter for game/device) unconditionally added field__in=value even when value was empty, and __in=[] matches no rows — so a filter with only excludes (e.g. device excludes 11, no game/device includes) returned zero results. Guard the empty value like ChoiceCriterion already does, so an exclude-only criterion means 'all rows except the excluded ids'. https://claude.ai/code/session_01XzhXvMvw42CQGc9kmin3GS
This commit is contained in:
+3
-1
@@ -277,7 +277,9 @@ class MultiCriterion(_Criterion):
|
||||
def to_q(self, field_name: str) -> Q:
|
||||
m = self.modifier
|
||||
if m == Modifier.INCLUDES:
|
||||
q = Q(**{f"{field_name}__in": self.value})
|
||||
q = Q()
|
||||
if self.value:
|
||||
q &= Q(**{f"{field_name}__in": self.value})
|
||||
if self.excludes:
|
||||
q &= ~Q(**{f"{field_name}__in": self.excludes})
|
||||
return q
|
||||
|
||||
@@ -10,6 +10,7 @@ from common.criteria import (
|
||||
ChoiceCriterion,
|
||||
IntCriterion,
|
||||
Modifier,
|
||||
MultiCriterion,
|
||||
StringCriterion,
|
||||
)
|
||||
from common.components import FilterBar
|
||||
@@ -98,6 +99,29 @@ class TestChoiceCriterion:
|
||||
assert c.to_q("status") == ~Q(status__in=["f"])
|
||||
|
||||
|
||||
class TestMultiCriterion:
|
||||
def test_includes(self):
|
||||
c = MultiCriterion(value=[797], modifier=Modifier.INCLUDES)
|
||||
assert c.to_q("game_id") == Q(game_id__in=[797])
|
||||
|
||||
def test_excludes_only_empty_value(self):
|
||||
"""Exclude one device with no includes — value=[], excludes=[11].
|
||||
|
||||
Regression: an empty ``value`` must not add ``__in=[]`` (which matches
|
||||
nothing); the criterion should mean "all rows except device 11".
|
||||
"""
|
||||
c = MultiCriterion(value=[], excludes=[11], modifier=Modifier.INCLUDES)
|
||||
assert c.to_q("device_id") == ~Q(device_id__in=[11])
|
||||
|
||||
def test_include_and_exclude(self):
|
||||
c = MultiCriterion(value=[1], excludes=[2], modifier=Modifier.INCLUDES)
|
||||
assert c.to_q("game_id") == Q(game_id__in=[1]) & ~Q(game_id__in=[2])
|
||||
|
||||
def test_is_null(self):
|
||||
c = MultiCriterion(value=[], modifier=Modifier.IS_NULL)
|
||||
assert c.to_q("device_id") == Q(device_id__isnull=True)
|
||||
|
||||
|
||||
class TestChoiceCriterionAgainstDB:
|
||||
"""Verify ChoiceCriterion produces correct DB results."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user