0001-variables-handle-filtering-lists-by-any-of-values-56.patch
tests/test_formdata.py | ||
---|---|---|
1567 | 1567 |
formdef.fields = [ |
1568 | 1568 |
fields.StringField(id='1', label='Test', type='string', varname='foo'), |
1569 | 1569 |
fields.StringField(id='2', label='key', type='string', varname='key'), |
1570 |
fields.ItemsField(id='3', label='Items', type='items', varname='items'), |
|
1570 | 1571 |
] |
1571 | 1572 |
formdef.store() |
1572 | 1573 |
formdef.data_class().wipe() |
1573 | 1574 |
for i in range(6): |
1574 | 1575 |
formdata = formdef.data_class()() |
1575 |
formdata.data = {'1': 'bar', '2': str(i)} |
|
1576 |
formdata.data = {'1': 'bar', '2': str(i), '3': [str(i % 2), str(i % 3)]}
|
|
1576 | 1577 |
formdata.just_created() |
1577 | 1578 |
formdata.store() |
1578 | 1579 |
for i in range(4): |
1579 | 1580 |
formdata = formdef.data_class()() |
1580 |
formdata.data = {'1': 'foo', '2': str(i + 6)} |
|
1581 |
formdata.data = {'1': 'foo', '2': str(i + 6), '3': [str(i)]}
|
|
1581 | 1582 |
formdata.just_created() |
1582 | 1583 |
formdata.jump_status('finished') |
1583 | 1584 |
formdata.store() |
... | ... | |
1595 | 1596 |
tmpl = Template('{{forms|objects:"foobarlazy"|filter_by:"foo"|filter_value:"bar"|count}}') |
1596 | 1597 |
assert tmpl.render(context) == '6' |
1597 | 1598 | |
1599 |
# filter value by multiple valued field |
|
1600 |
tmpl = Template('{{forms|objects:"foobarlazy"|filter_by:"items"|filter_value:"3"|count}}') |
|
1601 |
assert tmpl.render(context) == '1' |
|
1602 | ||
1603 |
tmpl = Template('{{forms|objects:"foobarlazy"|filter_by:"items"|filter_value:"2"|count}}') |
|
1604 |
assert tmpl.render(context) == '3' |
|
1605 | ||
1606 |
tmpl = Template('{{forms|objects:"foobarlazy"|filter_by:"items"|filter_value:"1|2"|count}}') |
|
1607 |
assert tmpl.render(context) == '7' |
|
1608 | ||
1609 |
tmpl = Template('{{forms|objects:"foobarlazy"|filter_by:"items"|filter_value:"6"|count}}') |
|
1610 |
assert tmpl.render(context) == '0' |
|
1611 | ||
1612 |
tmpl = Template('{{forms|objects:"foobarlazy"|filter_by:"items"|exclude_value:"1|2"|count}}') |
|
1613 |
assert tmpl.render(context) == '3' |
|
1614 | ||
1615 |
tmpl = Template('{{forms|objects:"foobarlazy"|filter_by:"items"|exclude_value:"6"|count}}') |
|
1616 |
assert tmpl.render(context) == '10' |
|
1617 | ||
1598 | 1618 |
pub.custom_view_class.wipe() |
1599 | 1619 | |
1600 | 1620 |
custom_view1 = pub.custom_view_class() |
wcs/qommon/storage.py | ||
---|---|---|
216 | 216 |
return func |
217 | 217 | |
218 | 218 | |
219 |
class NotIntersects(Criteria): |
|
220 |
def build_lambda(self): |
|
221 |
value = set(self.value) |
|
222 | ||
223 |
def func(x): |
|
224 |
try: |
|
225 |
return value.isdisjoint(set(getattr(x, self.attribute, []) or [])) |
|
226 |
except KeyError: |
|
227 |
# this may happen if used to check a formdata field that didn't |
|
228 |
# exist when the formdata was created. |
|
229 |
return False |
|
230 | ||
231 |
return func |
|
232 | ||
233 | ||
219 | 234 |
class Or(Criteria): |
220 | 235 |
def __init__(self, criterias, **kwargs): |
221 | 236 |
self.criterias = criterias |
wcs/sql.py | ||
---|---|---|
269 | 269 |
return {'c%s' % id(self.value): list(self.value)} |
270 | 270 | |
271 | 271 | |
272 |
class NotIntersects(Criteria): |
|
273 |
def as_sql(self): |
|
274 |
if not self.value: |
|
275 |
return 'ARRAY_LENGTH(%s, 1) IS NULL' % self.attribute |
|
276 |
else: |
|
277 |
return 'NOT(%s && %%(c%s)s)' % (self.attribute, id(self.value)) |
|
278 | ||
279 |
def as_sql_param(self): |
|
280 |
return {'c%s' % id(self.value): list(self.value)} |
|
281 | ||
282 | ||
272 | 283 |
class ILike(Criteria): |
273 | 284 |
def __init__(self, attribute, value, **kwargs): |
274 | 285 |
super().__init__(attribute, value, **kwargs) |
wcs/variables.py | ||
---|---|---|
25 | 25 |
from .formdef import FormDef |
26 | 26 |
from .qommon import _, force_str, misc |
27 | 27 |
from .qommon.evalutils import make_datetime |
28 |
from .qommon.storage import Equal, NotEqual, Null, Or
|
|
28 |
from .qommon.storage import Equal, Intersects, NotEqual, NotIntersects, Null, Or
|
|
29 | 29 |
from .qommon.substitution import CompatibilityNamesDict |
30 | 30 |
from .qommon.templatetags.qommon import parse_datetime |
31 | 31 | |
... | ... | |
202 | 202 |
from wcs import sql |
203 | 203 | |
204 | 204 |
field_id = sql.get_field_id(field) |
205 | ||
205 | 206 |
if exclude: |
206 |
criterias = NotEqual(field_id, value, field=field) |
|
207 |
if isinstance(value, list): |
|
208 |
criterias = NotIntersects(field_id, value, field=field) |
|
209 |
else: |
|
210 |
criterias = NotEqual(field_id, value, field=field) |
|
207 | 211 |
else: |
208 |
criterias = Equal(field_id, value, field=field) |
|
212 |
if isinstance(value, list): |
|
213 |
criterias = Intersects(field_id, value, field=field) |
|
214 |
else: |
|
215 |
criterias = Equal(field_id, value, field=field) |
|
216 | ||
209 | 217 |
return self._clone(self._criterias + [criterias]) |
210 | 218 | |
211 | 219 |
def apply_exclude_value(self, value): |
212 |
- |