예제 #1
0
    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)
예제 #2
0
    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)
예제 #3
0
    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, []
예제 #4
0
 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())
예제 #5
0
    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
예제 #6
0
 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)
예제 #7
0
    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)
예제 #8
0
 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())
예제 #9
0
    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)