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]). """ clean_data = [] errors = ErrorList() if self.required and not value: raise ValidationError(gettext(u'This field is required.')) elif not self.required and not value: return self.compress([]) if not isinstance(value, (list, tuple)): raise ValidationError(gettext(u'Enter a list of values.')) 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: raise ValidationError(gettext(u'This field is 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)
def clean(self): opts = self._meta self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude) try: self.instance.full_validate(exclude=self._errors.keys()) except ValidationError, e: for k, v in e.message_dict.items(): if k != NON_FIELD_ERRORS: self._errors.setdefault(k, ErrorList()).extend(v) # Remove the data from the cleaned_data dict since it was invalid if k in self.cleaned_data: del self.cleaned_data[k] if NON_FIELD_ERRORS in e.message_dict: raise ValidationError(e.message_dict[NON_FIELD_ERRORS]) # If model validation threw errors for fields that aren't on the # form, the the errors cannot be corrected by the user. Displaying # those errors would be pointless, so raise another type of # exception that *won't* be caught and displayed by the form. if set(e.message_dict.keys()) - set(self.fields.keys() + [NON_FIELD_ERRORS]): raise UnresolvableValidationError(e.message_dict)
def _perform_date_checks(self, date_checks): bad_fields = set() for lookup_type, field, unique_for in date_checks: lookup_kwargs = {} # there's a ticket to add a date lookup, we can remove this special # case if that makes it's way in if lookup_type == 'date': date = self.cleaned_data[unique_for] lookup_kwargs['%s__day' % unique_for] = date.day lookup_kwargs['%s__month' % unique_for] = date.month lookup_kwargs['%s__year' % unique_for] = date.year else: lookup_kwargs['%s__%s' % (unique_for, lookup_type)] = getattr(self.cleaned_data[unique_for], lookup_type) lookup_kwargs[field] = self.cleaned_data[field] qs = self.instance.__class__._default_manager.filter(**lookup_kwargs) # Exclude the current object from the query if we are editing an # instance (as opposed to creating a new one) if self.instance.pk is not None: qs = qs.exclude(pk=self.instance.pk) # This cute trick with extra/values is the most efficient way to # tell if a particular query returns any results. if qs.extra(select={'a': 1}).values('a').order_by(): self._errors[field] = ErrorList([ self.date_error_message(lookup_type, field, unique_for) ]) bad_fields.add(field) return bad_fields, []
def non_field_errors(self): """ Returns an ErrorList of errors that aren't associated with a particular field -- i.e., from Form.clean(). Returns an empty ErrorList if there are none. """ return self.errors.get(NON_FIELD_ERRORS, ErrorList())
def _perform_unique_checks(self, unique_checks): bad_fields = set() form_errors = [] for unique_check in unique_checks: # Try to look up an existing object with the same values as this # object's values for all the unique field. lookup_kwargs = {} for field_name in unique_check: lookup_kwargs[field_name] = self.cleaned_data[field_name] qs = self.instance.__class__._default_manager.filter( **lookup_kwargs) # Exclude the current object from the query if we are editing an # instance (as opposed to creating a new one) if self.instance.pk is not None: qs = qs.exclude(pk=self.instance.pk) # This cute trick with extra/values is the most efficient way to # tell if a particular query returns any results. if qs.extra(select={'a': 1}).values('a').order_by(): if len(unique_check) == 1: self._errors[unique_check[0]] = ErrorList( [self.unique_error_message(unique_check)]) else: form_errors.append(self.unique_error_message(unique_check)) # Mark these fields as needing to be removed from cleaned data # later. for field_name in unique_check: bad_fields.add(field_name) return bad_fields, form_errors
def _html_output(self, normal_row, error_row, row_ender, help_text_html, errors_on_separate_row): "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()." top_errors = self.non_field_errors( ) # Errors that should be displayed above all fields. output, hidden_fields = [], [] for name, field in self.fields.items(): bf = BoundField(self, field, name) bf_errors = ErrorList([escape(error) for error in bf.errors ]) # Escape and cache in local variable. if bf.is_hidden: if bf_errors: top_errors.extend([ '(Hidden field %s) %s' % (name, e) for e in bf_errors ]) hidden_fields.append(unicode(bf)) else: if errors_on_separate_row and bf_errors: output.append(error_row % bf_errors) if bf.label: label = escape(bf.label) # Only add a colon if the label does not end in punctuation. if label[-1] not in ':?.!': label += ':' label = bf.label_tag(label) or '' else: label = '' if field.help_text: help_text = help_text_html % field.help_text else: help_text = u'' output.append( normal_row % { 'errors': bf_errors, 'label': label, 'field': unicode(bf), 'help_text': help_text }) if top_errors: output.insert(0, error_row % top_errors) if hidden_fields: # Insert any hidden fields in the last row. str_hidden = u''.join(hidden_fields) if output: last_row = output[-1] # Chop off the trailing row_ender (e.g. '</td></tr>') and insert the hidden fields. output[ -1] = last_row[:-len(row_ender)] + str_hidden + row_ender else: # If there aren't any rows in the output, just append the hidden fields. output.append(str_hidden) return u'\n'.join(output)
def validate_unique(self): from django.db.models.fields import FieldDoesNotExist # Gather a list of checks to perform. We only perform unique checks # for fields present and not None in cleaned_data. Since this is a # ModelForm, some fields may have been excluded; we can't perform a unique # check on a form that is missing fields involved in that check. It also does # not make sense to check data that didn't validate, and since NULL does not # equal NULL in SQL we should not do any unique checking for NULL values. unique_checks = [] for check in self.instance._meta.unique_together[:]: fields_on_form = [ field for field in check if self.cleaned_data.get(field) is not None ] if len(fields_on_form) == len(check): unique_checks.append(check) form_errors = [] # Gather a list of checks for fields declared as unique and add them to # the list of checks. Again, skip empty fields and any that did not validate. for name, field in self.fields.items(): try: f = self.instance._meta.get_field_by_name(name)[0] except FieldDoesNotExist: # This is an extra field that's not on the ModelForm, ignore it continue if f.unique and self.cleaned_data.get(name) is not None: unique_checks.append((name, )) bad_fields = set() for unique_check in unique_checks: # Try to look up an existing object with the same values as this # object's values for all the unique field. lookup_kwargs = {} for field_name in unique_check: lookup_kwargs[field_name] = self.cleaned_data[field_name] qs = self.instance.__class__._default_manager.filter( **lookup_kwargs) # Exclude the current object from the query if we are editing an # instance (as opposed to creating a new one) if self.instance.pk is not None: qs = qs.exclude(pk=self.instance.pk) # This cute trick with extra/values is the most efficient way to # tell if a particular query returns any results. if qs.extra(select={'a': 1}).values('a').order_by(): model_name = capfirst(self.instance._meta.verbose_name) # A unique field if len(unique_check) == 1: field_name = unique_check[0] field_label = self.fields[field_name].label # Insert the error into the error dict, very sneaky self._errors[field_name] = ErrorList([ _(u"%(model_name)s with this %(field_label)s already exists.") % \ {'model_name': unicode(model_name), 'field_label': unicode(field_label)} ]) # unique_together else: field_labels = [ self.fields[field_name].label for field_name in unique_check ] field_labels = get_text_list(field_labels, _('and')) form_errors.append( _(u"%(model_name)s with this %(field_label)s already exists.") % \ {'model_name': unicode(model_name), 'field_label': unicode(field_labels)} ) # Mark these fields as needing to be removed from cleaned data # later. for field_name in unique_check: bad_fields.add(field_name) for field_name in bad_fields: del self.cleaned_data[field_name] if form_errors: # Raise the unique together errors since they are considered # form-wide. raise ValidationError(form_errors)
def _errors(self): """ Returns an ErrorList for this field. Returns an empty ErrorList if there are none. """ return self.form.errors.get(self.name, ErrorList())
def validate_unique(self): from django.db.models.fields import FieldDoesNotExist # Gather a list of checks to perform. Since this is a ModelForm, some # fields may have been excluded; we can't perform a unique check on a # form that is missing fields involved in that check. unique_checks = [] for check in self.instance._meta.unique_together[:]: fields_on_form = [field for field in check if field in self.fields] if len(fields_on_form) == len(check): unique_checks.append(check) form_errors = [] # Gather a list of checks for fields declared as unique and add them to # the list of checks. Again, skip fields not on the form. for name, field in self.fields.items(): try: f = self.instance._meta.get_field_by_name(name)[0] except FieldDoesNotExist: # This is an extra field that's not on the ModelForm, ignore it continue # MySQL can't handle ... WHERE pk IS NULL, so make sure we # don't generate queries of that form. is_null_pk = f.primary_key and self.cleaned_data[name] is None if name in self.cleaned_data and f.unique and not is_null_pk: unique_checks.append((name, )) # Don't run unique checks on fields that already have an error. unique_checks = [ check for check in unique_checks if not [x in self._errors for x in check if x in self._errors] ] bad_fields = set() for unique_check in unique_checks: # Try to look up an existing object with the same values as this # object's values for all the unique field. lookup_kwargs = {} for field_name in unique_check: lookup_kwargs[field_name] = self.cleaned_data[field_name] qs = self.instance.__class__._default_manager.filter( **lookup_kwargs) # Exclude the current object from the query if we are editing an # instance (as opposed to creating a new one) if self.instance.pk is not None: qs = qs.exclude(pk=self.instance.pk) # This cute trick with extra/values is the most efficient way to # tell if a particular query returns any results. if qs.extra(select={'a': 1}).values('a').order_by(): model_name = capfirst(self.instance._meta.verbose_name) # A unique field if len(unique_check) == 1: field_name = unique_check[0] field_label = self.fields[field_name].label # Insert the error into the error dict, very sneaky self._errors[field_name] = ErrorList([ _(u"%(model_name)s with this %(field_label)s already exists.") % \ {'model_name': unicode(model_name), 'field_label': unicode(field_label)} ]) # unique_together else: field_labels = [ self.fields[field_name].label for field_name in unique_check ] field_labels = get_text_list(field_labels, _('and')) form_errors.append( _(u"%(model_name)s with this %(field_label)s already exists.") % \ {'model_name': unicode(model_name), 'field_label': unicode(field_labels)} ) # Mark these fields as needing to be removed from cleaned data # later. for field_name in unique_check: bad_fields.add(field_name) for field_name in bad_fields: del self.cleaned_data[field_name] if form_errors: # Raise the unique together errors since they are considered # form-wide. raise ValidationError(form_errors)