from django.forms.fields import MultiValueField, EMPTY_VALUES from django.forms.util import ErrorList class PartialRequiredMultiValueField(MultiValueField): """ Overwrites the default django multivalue field. Instead of checking the required for ALL fields we don't throw errors """ fields_required = [] def __init__(self, fields=(), *args, **kwargs): for f in fields: self.fields_required.append(bool(f.required)) self.fields = fields super(MultiValueField, self).__init__(fields, *args, **kwargs) def clean(self, value): """ Validates every value in the given list. A value is validated against the corresponding Field in self.fields. For example, if this MultiValueField was instantiated with fields=(DateField(), TimeField()), clean() would call DateField.clean(value[0]) and TimeField.clean(value[1]). Added the field specific required check """ clean_data = [] errors = ErrorList() if not value or isinstance(value, (list, tuple)): if not value or not [v for v in value if v not in EMPTY_VALUES]: if self.required: raise ValidationError(self.error_messages['required']) else: return self.compress([]) else: raise ValidationError(self.error_messages['invalid']) for i, field in enumerate(self.fields): try: field_value = value[i] except IndexError: field_value = None if self.required and field_value in EMPTY_VALUES and self.fields_required[i]: raise ValidationError(self.error_messages['required']) try: clean_data.append(field.clean(field_value)) except ValidationError, e: # Collect all validation errors in a single list, which we'll # raise at the end of clean(), rather than raising a single # exception for the first error we encounter. errors.extend(e.messages) if errors: raise ValidationError(errors) return self.compress(clean_data)