Skip to content

Commit

Permalink
Merge branch 'master' into fix/field-mapping
Browse files Browse the repository at this point in the history
# Conflicts:
#	formly/tests/tests.py
  • Loading branch information
jacobwegner committed Nov 9, 2017
2 parents 38c4bdf + 89e24a2 commit 0b2bf02
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 1 deletion.
25 changes: 25 additions & 0 deletions formly/fields.py
@@ -0,0 +1,25 @@
from django import forms
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _


class LimitedMultipleChoiceField(forms.MultipleChoiceField):
def __init__(self, *args, **kwargs):
self.maximum_choices = kwargs.pop("maximum_choices")

self.default_error_messages.update({
'maximum_choices': _('You may select at most %(maximum)d choices (%(selected)d selected)')
})

super(LimitedMultipleChoiceField, self).__init__(*args, **kwargs)

def validate(self, value):
super(LimitedMultipleChoiceField, self).validate(value)

selected_count = len(value)
if self.maximum_choices and selected_count > self.maximum_choices:
raise ValidationError(
self.error_messages['maximum_choices'],
code='maximum_choices',
params={'maximum': self.maximum_choices, 'selected': selected_count},
)
5 changes: 4 additions & 1 deletion formly/models.py
Expand Up @@ -12,6 +12,7 @@

from jsonfield import JSONField

from .fields import LimitedMultipleChoiceField
from .forms import MultipleTextField, MultiTextWidget
from .forms.widgets import LikertSelect, RatingSelect

Expand Down Expand Up @@ -362,6 +363,8 @@ def _get_field_class(self, choices):

if self.field_type in [Field.CHECKBOX_FIELD, Field.SELECT_FIELD, Field.RADIO_CHOICES, Field.LIKERT_FIELD, Field.RATING_FIELD]:
kwargs.update({"choices": choices})
if self.field_type == Field.CHECKBOX_FIELD:
kwargs.update({"maximum_choices": self.maximum_choices})
elif self.field_type == Field.MULTIPLE_TEXT:
kwargs.update({
"fields_length": self.expected_answers,
Expand Down Expand Up @@ -406,7 +409,7 @@ def _get_field_class(self, choices):
)
),
Field.CHECKBOX_FIELD: dict(
field_class=forms.MultipleChoiceField,
field_class=LimitedMultipleChoiceField,
kwargs=dict(
widget=forms.CheckboxSelectMultiple()
)
Expand Down
27 changes: 27 additions & 0 deletions formly/tests/tests.py
@@ -1,9 +1,11 @@
from django.core.exceptions import ValidationError
from django.contrib.auth import get_user_model
from django.test import TestCase

from ..models import (
Field,
Survey,
FieldChoice,
)

User = get_user_model()
Expand Down Expand Up @@ -44,3 +46,28 @@ def test_text_field_form_field_render(self):
)
# Ensure no exception when field is instantiated
self.assertTrue(field.form_field())

def test_multiplechoice_field_choice_limit(self):
"""
Enforce maximum_choices on multiple choice fields that allow
multiple answers.
"""
survey = Survey.objects.create(
name="multiple choice test",
creator=self.user,
)
field = Field.objects.create(
survey=survey,
label="multiple choice field",
field_type=Field.CHECKBOX_FIELD,
maximum_choices=1,
ordinal=0,
)
choice_pks = []
for label in ["a", "b"]:
choice = FieldChoice.objects.create(label=label, field=field)
choice_pks.append(choice.pk)

form_field = field.form_field()
with self.assertRaises(ValidationError):
form_field.clean(choice_pks)

0 comments on commit 0b2bf02

Please sign in to comment.