misc: extend |filter_by to work with fields with non-unique varname (#56614)

This commit is contained in:
Frédéric Péters 2021-10-19 15:18:42 +02:00
parent c1ccadde40
commit 8fca1ed0b5
2 changed files with 81 additions and 20 deletions

View File

@ -1277,17 +1277,17 @@ def test_lazy_formdata_queryset_filter(pub, variable_test_data):
queryset = lazy_formdata.objects.filter_by('datefield').apply_exclude_value(
datetime.date(2018, 7, 31).timetuple()
)
assert queryset.count == 1
assert queryset.count == 6 # 1 + 5 null
queryset = lazy_formdata.objects.filter_by('datefield').apply_exclude_value(
datetime.date(2018, 7, 31)
)
assert queryset.count == 1
assert queryset.count == 6
queryset = lazy_formdata.objects.filter_by('datefield').apply_exclude_value(
datetime.datetime(2018, 7, 31)
)
assert queryset.count == 1
assert queryset.count == 6
queryset = lazy_formdata.objects.filter_by('datefield').apply_exclude_value('2018-07-31')
assert queryset.count == 1
assert queryset.count == 6
queryset = lazy_formdata.objects.filter_by('datefield').apply_exclude_value('still not a date')
assert queryset.count == 0
assert pub.loggederror_class.count() == 3
@ -1394,7 +1394,7 @@ def test_lazy_formdata_queryset_filter(pub, variable_test_data):
tmpl = Template('{{form_objects|filter_by:"foo_foo"|exclude_value:form_var_foo_foo|count}}')
assert tmpl.render(context) == '4'
tmpl = Template('{{form_objects|filter_by:"datefield"|exclude_value:form_var_datefield|count}}')
assert tmpl.render(context) == '1'
assert tmpl.render(context) == '6'
tmpl = Template('{{form_objects|filter_by:"boolfield"|exclude_value:form_var_boolfield|count}}')
assert tmpl.render(context) == '6'
tmpl = Template('{{form_objects|filter_by:"term1"|exclude_value:form_var_term1|count}}')
@ -1547,6 +1547,51 @@ def test_lazy_formdata_queryset_filter(pub, variable_test_data):
assert 'not a date' not in LazyFormData(formdata).objects.getlist('datefield')
def test_lazy_formdata_queryset_filter_non_unique_varname(pub, variable_test_data):
lazy_formdata = variable_test_data
formdef = lazy_formdata._formdef
# modify fields to have foo_foo as varname for both fields[0] and fields[7]
assert formdef.fields[7].label == 'string2'
formdef.fields[7].varname = 'foo_foo'
formdef.store()
data_class = lazy_formdata._formdef.data_class()
for i in range(6):
formdata = data_class()
formdata.data = {'0': 'bar', '6': 'baz'}
if i == 5:
formdata.data['3'] = datetime.date(2018, 8, 31).timetuple()
formdata.just_created()
formdata.store()
formdatas = []
for _ in range(4):
formdata = data_class()
formdata.data = {
'0': 'foo',
}
formdata.just_created()
formdata.jump_status('finished')
formdata.store()
formdatas.append(formdata)
context = pub.substitutions.get_context_variables(mode='lazy')
tmpl = Template('{{form_objects|filter_by:"foo_foo"|filter_value:"bar"|count}}')
assert tmpl.render(context) == '7'
tmpl = Template('{{form_objects|filter_by:"foo_foo"|filter_value:"other"|count}}')
assert tmpl.render(context) == '1'
tmpl = Template('{{form_objects|filter_by:"foo_foo"|filter_value:"baz"|count}}')
assert tmpl.render(context) == '6'
tmpl = Template('{{form_objects|filter_by:"foo_foo"|exclude_value:"bar"|count}}')
assert tmpl.render(context) == '4' # 11 - 7
for formdata in formdatas[:-1]:
formdata.data['6'] = 'bar'
formdata.store()
tmpl = Template('{{form_objects|filter_by:"foo_foo"|exclude_value:"bar"|count}}')
assert tmpl.render(context) == '1'
def test_lazy_formdata_queryset_get_from_first(pub, variable_test_data):
context = pub.substitutions.get_context_variables(mode='lazy')
del context['form'] # remove actual form from context

View File

@ -26,7 +26,7 @@ from .formdata import get_workflow_roles_substitution_variables
from .formdef import FormDef
from .qommon import _, force_str, misc
from .qommon.evalutils import make_datetime
from .qommon.storage import Equal, Intersects, Not, NotEqual, Null, Or
from .qommon.storage import And, Equal, Intersects, Not, NotEqual, Null, Or
from .qommon.substitution import CompatibilityNamesDict
from .qommon.templatetags.qommon import parse_datetime
@ -175,24 +175,30 @@ class LazyFormDefObjectsManager:
def filter_by_number(self, value):
return self._clone(self._criterias + [Equal('id_display', str(value))])
def get_field(self, key):
def get_fields(self, key):
for field in self._formdef.get_all_fields():
if getattr(field, 'varname', None) == key:
return field
yield field
def get_field(self, key):
for field in self.get_fields(key):
return field
def apply_filter_value(self, value, exclude=False):
assert self.pending_attr
field = self.get_field(self.pending_attr)
if field is None:
fields = list(self.get_fields(self.pending_attr))
if not fields:
get_publisher().record_error(
_('Invalid filter "%s"') % self.pending_attr, formdata=self._formdata
)
return self.none()
if field.convert_value_from_anything:
if fields[0].convert_value_from_anything:
# consider all fields with same varname are of the same type
# (it should definitely be)
try:
value = field.convert_value_from_anything(value)
value = fields[0].convert_value_from_anything(value)
except (ValueError, AttributeError):
get_publisher().record_error(
_('Invalid value "%s" for filter "%s"') % (value, self.pending_attr),
@ -202,16 +208,26 @@ class LazyFormDefObjectsManager:
from wcs import sql
field_id = sql.get_field_id(field)
if isinstance(value, list):
criterias = Intersects(field_id, value, field=field)
else:
criterias = Equal(field_id, value, field=field)
criterias = []
for field in fields:
field_id = sql.get_field_id(field)
if isinstance(value, list):
criteria = Intersects(field_id, value, field=field)
if exclude:
criteria = Not(criteria)
elif exclude:
criteria = Or([NotEqual(field_id, value, field=field), Null(field_id)])
else:
criteria = Equal(field_id, value, field=field)
criterias.append(criteria)
if exclude:
criterias = Not(criterias)
if len(criterias) > 1:
if exclude:
criterias = [And(criterias)]
else:
criterias = [Or(criterias)]
return self._clone(self._criterias + [criterias])
return self._clone(self._criterias + criterias)
def apply_exclude_value(self, value):
return self.apply_filter_value(value, exclude=True)