from django.utils.translation import ugettext_lazy as _ from django.utils.encoding import smart_unicode, smart_str from django.forms import Field, CheckboxInput, ChoiceField, MultipleHiddenInput, CheckboxSelectMultiple from django.forms.util import ErrorList, ValidationError class CheckboxField(Field): widget = CheckboxInput default_error_messages = { 'invalid_choice': _(u'Select a valid choice. %(value)s is not one of the available choices.'), } def __init__(self, choices=(), required=True, widget=None, label=None, initial=None, help_text=None, *args, **kwargs): super(ChoiceField, self).__init__(required, widget, label, initial, help_text, *args, **kwargs) self.choices = choices def _get_choices(self): return self._choices def _set_choices(self, value): # Setting choices also sets the choices on the widget. # choices can be any iterable, but we call list() on it because # it will be consumed more than once. self._choices = self.widget.choices = list(value) choices = property(_get_choices, _set_choices) def clean(self, value): """ Validates that the input is in self.choices. """ value = super(ChoiceField, self).clean(value) if value in EMPTY_VALUES: value = u'' value = smart_unicode(value) if value == u'': return value if not self.valid_value(value): raise ValidationError(self.error_messages['invalid_choice'] % {'value': value}) return value def valid_value(self, value): "Check to see if the provided value is a valid choice" for k, v in self.choices: if type(v) in (tuple, list): # This is an optgroup, so look inside the group for options for k2, v2 in v: if value == smart_unicode(k2): return True else: if value == smart_unicode(k): return True return False class MultipleCheckboxField(ChoiceField): hidden_widget = MultipleHiddenInput widget = CheckboxSelectMultiple default_error_messages = { 'invalid_choice': _(u'Select a valid choice. %(value)s is not one of the available choices.'), 'invalid_list': _(u'Enter a list of values.'), } def clean(self, value): """ Validates that the input is a list or tuple. """ if self.required and not value: raise ValidationError(self.error_messages['required']) elif not self.required and not value: return [] if not isinstance(value, (list, tuple)): raise ValidationError(self.error_messages['invalid_list']) new_value = [smart_unicode(val) for val in value] # Validate that each value in the value list is in self.choices. for val in new_value: if not self.valid_value(val): raise ValidationError(self.error_messages['invalid_choice'] % {'value': val}) return new_value