fields: add min_choices option to ItemsField (#49896)

This commit is contained in:
Lauréline Guérin 2021-01-08 10:09:58 +01:00
parent b918c88f0f
commit e14acb7d05
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
4 changed files with 43 additions and 7 deletions

View File

@ -1679,6 +1679,8 @@ def test_form_edit_items_field(pub):
resp = resp.click('Edit', href='1/')
assert resp.forms[0]['label'].value == '1st field'
assert resp.forms[0]['min_choices'].value == '0'
assert resp.forms[0]['max_choices'].value == '0'
resp.forms[0]['label'] = 'changed field'
resp.forms[0]['required'] = False
resp = resp.forms[0].submit('items$add_element')
@ -1691,16 +1693,22 @@ def test_form_edit_items_field(pub):
resp = resp.follow()
assert FormDef.get(1).fields[0].label == 'changed field'
assert FormDef.get(1).fields[0].required == False
assert FormDef.get(1).fields[0].required is False
assert FormDef.get(1).fields[0].items is None
assert FormDef.get(1).fields[0].min_choices == 0
assert FormDef.get(1).fields[0].max_choices == 0
# edit and fill with one item
resp = resp.click('Edit', href='1/')
assert resp.forms[0]['label'].value == 'changed field'
resp.forms[0]['items$element0'] = 'XXX'
resp.forms[0]['min_choices'] = 2
resp.forms[0]['max_choices'] = 5
resp = resp.forms[0].submit('submit')
assert resp.location == 'http://example.net/backoffice/forms/1/fields/#itemId_1'
assert FormDef.get(1).fields[0].items == ['XXX']
assert FormDef.get(1).fields[0].min_choices == 2
assert FormDef.get(1).fields[0].max_choices == 5
# check prefilling is only possible with Python
resp = resp.follow()

View File

@ -526,11 +526,13 @@ def test_form_string_field_submit(pub):
def test_form_items_submit(pub):
formdef = create_formdef()
formdef.fields = [fields.ItemsField(id='0', label='items', type='items',
required=True,
varname='foo', items=['Foo', 'Bar', 'Three']),]
formdef.fields = [fields.ItemsField(
id='0', label='items', type='items',
required=True,
varname='foo', items=['Foo', 'Bar', 'Three', 'Four', 'Five', 'Six'])]
formdef.store()
formdef.data_class().wipe()
page = get_app(pub).get('/test/')
next_page = page.forms[0].submit('submit') # but the field is required
assert next_page.pyquery('div.error').text() == 'required field'
@ -548,6 +550,25 @@ def test_form_items_submit(pub):
assert data.data['0'] == ['Foo', 'Bar']
assert data.data['0_display'] == 'Foo, Bar'
formdef.fields[0].min_choices = 2
formdef.fields[0].max_choices = 5
formdef.store()
page = get_app(pub).get('/test/')
page.forms[0]['f0$element0'].checked = True
page = page.forms[0].submit('submit')
assert page.pyquery('div.error').text() == 'You must select at least 2 answers.'
page.forms[0]['f0$element1'].checked = True
page.forms[0]['f0$element2'].checked = True
page.forms[0]['f0$element3'].checked = True
page.forms[0]['f0$element4'].checked = True
page.forms[0]['f0$element5'].checked = True
page = page.forms[0].submit('submit')
assert page.pyquery('div.error').text() == 'You must select at most 5 answers.'
page.forms[0]['f0$element5'].checked = False
page = next_page.forms[0].submit('submit').follow()
assert 'The form has been recorded' in page.text
def test_form_string_with_invalid_xml_chars(pub):
formdef = create_formdef()

View File

@ -1791,6 +1791,7 @@ class ItemsField(WidgetField):
allow_complex = True
items = []
min_choices = 0
max_choices = 0
data_source = {}
in_filters = False
@ -1817,6 +1818,7 @@ class ItemsField(WidgetField):
def perform_more_widget_changes(self, form, kwargs, edit = True):
kwargs['options'] = self.get_options()
kwargs['min_choices'] = self.min_choices
kwargs['max_choices'] = self.max_choices
if self.data_source:
items = data_sources.get_items(self.data_source,
@ -1835,6 +1837,8 @@ class ItemsField(WidgetField):
value = self.items, required = False,
element_kwargs = {'render_br': False, 'size': 50},
add_element_label = _('Add item'))
form.add(IntWidget, 'min_choices', title=_('Minimum number of choices'),
value=self.min_choices, required=False, size=4)
form.add(IntWidget, 'max_choices', title = _('Maximum number of choices'),
value = self.max_choices, required = False, size = 4)
form.add(data_sources.DataSourceSelectionWidget, 'data_source',
@ -1850,9 +1854,9 @@ class ItemsField(WidgetField):
advanced=not(self.display_disabled_items))
def get_admin_attributes(self):
return WidgetField.get_admin_attributes(self) + ['items',
'max_choices', 'data_source', 'in_filters', 'anonymise',
'display_disabled_items']
return WidgetField.get_admin_attributes(self) + [
'items', 'min_choices', 'max_choices', 'data_source', 'in_filters', 'anonymise',
'display_disabled_items']
def check_admin_form(self, form):
items = form.get_widget('items').parse()

View File

@ -1349,6 +1349,7 @@ class CheckboxesWidget(CompositeWidget):
del kwargs['readonly']
self.readonly = True
self.inline = kwargs.get('inline', True)
self.min_choices = int(kwargs.get('min_choices', 0) or 0)
self.max_choices = int(kwargs.get('max_choices', 0) or 0)
self.options_with_attributes = kwargs.pop('options_with_attributes', None)
@ -1393,6 +1394,8 @@ class CheckboxesWidget(CompositeWidget):
self.value = values
if self.required and not self.value:
self.set_error(self.REQUIRED_ERROR)
if self.value and self.min_choices and len(self.value) < self.min_choices:
self.set_error(_('You must select at least %d answers.') % self.min_choices)
if self.value and self.max_choices and len(self.value) > self.max_choices:
self.set_error(_('You must select at most %d answers.') % self.max_choices)