예제 #1
0
 def clean(self, value):
     cleaned_data = []
     errors = []
     if not any(value) and self.required:
         raise ValidationError(self.error_messages['required'])
     max_size = max(self.size, len(value))
     for index in range(max_size):
         item = value[index]
         try:
             cleaned_data.append(self.base_field.clean(item))
         except ValidationError as error:
             errors.append(
                 prefix_validation_error(
                     error,
                     self.error_messages['item_invalid'],
                     code='item_invalid',
                     params={'nth': index + 1},
                 ))
             cleaned_data.append(None)
         else:
             errors.append(None)
     if self.remove_trailing_nulls:
         null_index = None
         for i, value in reversed(list(enumerate(cleaned_data))):
             if value in self.base_field.empty_values:
                 null_index = i
             else:
                 break
         if null_index is not None:
             cleaned_data = cleaned_data[:null_index]
             errors = errors[:null_index]
     errors = list(filter(None, errors))
     if errors:
         raise ValidationError(list(chain.from_iterable(errors)))
     return cleaned_data
예제 #2
0
def prefix_validation_error(error, prefix, code, params):
    """
    Prefix a validation error message while maintaining the existing
    validation data structure.
    """
    if error.error_list == [error]:
        error_params = error.params or {}
        return ValidationError(
            # We can't simply concatenate messages since they might require
            # their associated parameters to be expressed correctly which
            # is not something `format_lazy` does. For example, proxied
            # ngettext calls require a count parameter and are converted
            # to an empty string if they are missing it.
            message=format_lazy(
                '{} {}',
                SimpleLazyObject(lambda: prefix % params),
                SimpleLazyObject(lambda: error.message % error_params),
            ),
            code=code,
            params={
                **error_params,
                **params
            },
        )
    return ValidationError([
        prefix_validation_error(e, prefix, code, params)
        for e in error.error_list
    ])
예제 #3
0
    def clean(self, value):
        """
        Validate 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 = []
        if self.disabled and not isinstance(value, list):
            value = self.widget.decompress(value)
        if not value or isinstance(value, (list, tuple)):
            if not value or not [
                    v for v in value if v not in self.empty_values
            ]:
                if self.required:
                    raise ValidationError(self.error_messages['required'],
                                          code='required')
                else:
                    return self.compress([])
        else:
            raise ValidationError(self.error_messages['invalid'],
                                  code='invalid')
        for i, field in enumerate(self.fields):
            try:
                field_value = value[i]
            except IndexError:
                field_value = None
            if field_value in self.empty_values:
                if self.require_all_fields:
                    # Raise a 'required' error if the MultiValueField is
                    # required and any field is empty.
                    if self.required:
                        raise ValidationError(self.error_messages['required'],
                                              code='required')
                elif field.required:
                    # Otherwise, add an 'incomplete' error to the list of
                    # collected errors and skip field cleaning, if a required
                    # field is empty.
                    if field.error_messages['incomplete'] not in errors:
                        errors.append(field.error_messages['incomplete'])
                    continue
            try:
                clean_data.append(field.clean(field_value))
            except ValidationError as 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. Skip duplicates.
                errors.extend(m for m in e.error_list if m not in errors)
        if errors:
            raise ValidationError(errors)

        out = self.compress(clean_data)
        self.validate(out)
        self.run_validators(out)
        return out
예제 #4
0
 def validate(self, value):
     """Validate that the input is a list or tuple."""
     if self.required and not value:
         raise ValidationError(self.error_messages['required'],
                               code='required')
     # Validate that each value in the value list is in self.choices.
     for val in value:
         if not self.valid_value(val):
             raise ValidationError(
                 self.error_messages['invalid_choice'],
                 code='invalid_choice',
                 params={'value': val},
             )
예제 #5
0
 def compress(self, data_list):
     if data_list:
         # Raise a validation error if time or date is empty
         # (possible if SplitDateTimeField has required=False).
         if data_list[0] in self.empty_values:
             raise ValidationError(self.error_messages['invalid_date'],
                                   code='invalid_date')
         if data_list[1] in self.empty_values:
             raise ValidationError(self.error_messages['invalid_time'],
                                   code='invalid_time')
         result = datetime.datetime.combine(*data_list)
         return from_current_timezone(result)
     return None
예제 #6
0
 def to_python(self, value):
     if not value:
         return []
     elif not isinstance(value, (list, tuple)):
         raise ValidationError(self.error_messages['invalid_list'],
                               code='invalid_list')
     return [str(val) for val in value]
예제 #7
0
 def validate(self, value):
     super().validate(value)
     if value in self.empty_values:
         return
     if not value.is_finite():
         raise ValidationError(self.error_messages['invalid'],
                               code='invalid')
예제 #8
0
 def to_python(self, value):
     if value in self.empty_values:
         return None
     if isinstance(value, datetime.timedelta):
         return value
     try:
         value = parse_duration(str(value))
     except OverflowError:
         raise ValidationError(self.error_messages['overflow'].format(
             min_days=datetime.timedelta.min.days,
             max_days=datetime.timedelta.max.days,
         ),
                               code='overflow')
     if value is None:
         raise ValidationError(self.error_messages['invalid'],
                               code='invalid')
     return value
예제 #9
0
    def add_error(self, field, error):
        """
        Update the content of `self._errors`.

        The `field` argument is the name of the field to which the errors
        should be added. If it's None, treat the errors as NON_FIELD_ERRORS.

        The `error` argument can be a single error, a list of errors, or a
        dictionary that maps field names to lists of errors. An "error" can be
        either a simple string or an instance of ValidationError with its
        message attribute set and a "list or dictionary" can be an actual
        `list` or `dict` or an instance of ValidationError with its
        `error_list` or `error_dict` attribute set.

        If `error` is a dictionary, the `field` argument *must* be None and
        errors will be added to the fields that correspond to the keys of the
        dictionary.
        """
        if not isinstance(error, ValidationError):
            # Normalize to ValidationError and let its constructor
            # do the hard work of making sense of the input.
            error = ValidationError(error)

        if hasattr(error, 'error_dict'):
            if field is not None:
                raise TypeError(
                    "The argument `field` must be `None` when the `error` "
                    "argument contains errors for multiple fields.")
            else:
                error = error.error_dict
        else:
            error = {field or NON_FIELD_ERRORS: error.error_list}

        for field, error_list in error.items():
            if field not in self.errors:
                if field != NON_FIELD_ERRORS and field not in self.fields:
                    raise ValueError("'%s' has no field named '%s'." %
                                     (self.__class__.__name__, field))
                if field == NON_FIELD_ERRORS:
                    self._errors[field] = self.error_class(
                        error_class='nonfield')
                else:
                    self._errors[field] = self.error_class()
            self._errors[field].extend(error_list)
            if field in self.cleaned_data:
                del self.cleaned_data[field]
예제 #10
0
 def __call__(self, value):
     keys = set(value)
     missing_keys = self.keys - keys
     if missing_keys:
         raise ValidationError(
             self.messages['missing_keys'],
             code='missing_keys',
             params={'keys': ', '.join(missing_keys)},
         )
     if self.strict:
         extra_keys = keys - self.keys
         if extra_keys:
             raise ValidationError(
                 self.messages['extra_keys'],
                 code='extra_keys',
                 params={'keys': ', '.join(extra_keys)},
             )
예제 #11
0
 def to_python(self, value):
     value = value.strip()
     # Try to strptime against each input format.
     for format in self.input_formats:
         try:
             return self.strptime(value, format)
         except (ValueError, TypeError):
             continue
     raise ValidationError(self.error_messages['invalid'], code='invalid')
예제 #12
0
def validate_ipv46_address(value):
    try:
        validate_ipv4_address(value)
    except ValidationError:
        try:
            validate_ipv6_address(value)
        except ValidationError:
            raise ValidationError(_('Enter a valid IPv4 or IPv6 address.'),
                                  code='invalid')
예제 #13
0
 def __call__(self, value):
     cleaned = self.clean(value)
     params = {
         'limit_value': self.limit_value,
         'show_value': cleaned,
         'value': value
     }
     if self.compare(cleaned, self.limit_value):
         raise ValidationError(self.message, code=self.code, params=params)
예제 #14
0
 def validate(self, value):
     """Validate that the input is in self.choices."""
     super().validate(value)
     if value and not self.valid_value(value):
         raise ValidationError(
             self.error_messages['invalid_choice'],
             code='invalid_choice',
             params={'value': value},
         )
예제 #15
0
 def __call__(self, value):
     """
     Validate that the input contains (or does *not* contain, if
     inverse_match is True) a match for the regular expression.
     """
     regex_matches = self.regex.search(str(value))
     invalid_input = regex_matches if self.inverse_match else not regex_matches
     if invalid_input:
         raise ValidationError(self.message, code=self.code)
예제 #16
0
    def full_clean(self):
        """
        Clean all of self.data and populate self._errors and
        self._non_form_errors.
        """
        self._errors = []
        self._non_form_errors = self.error_class()
        empty_forms_count = 0

        if not self.is_bound:  # Stop further processing.
            return
        for i in range(0, self.total_form_count()):
            form = self.forms[i]
            # Empty forms are unchanged forms beyond those with initial data.
            if not form.has_changed() and i >= self.initial_form_count():
                empty_forms_count += 1
            # Accessing errors calls full_clean() if necessary.
            # _should_delete_form() requires cleaned_data.
            form_errors = form.errors
            if self.can_delete and self._should_delete_form(form):
                continue
            self._errors.append(form_errors)
        try:
            if (self.validate_max and
                    self.total_form_count() - len(self.deleted_forms) > self.max_num) or \
                    self.management_form.cleaned_data[TOTAL_FORM_COUNT] > self.absolute_max:
                raise ValidationError(
                    ngettext("Please submit %d or fewer forms.",
                             "Please submit %d or fewer forms.", self.max_num)
                    % self.max_num,
                    code='too_many_forms',
                )
            if (self.validate_min
                    and self.total_form_count() - len(self.deleted_forms) -
                    empty_forms_count < self.min_num):
                raise ValidationError(
                    ngettext("Please submit %d or more forms.",
                             "Please submit %d or more forms.", self.min_num) %
                    self.min_num,
                    code='too_few_forms')
            # Give self.clean() a chance to do cross-form validation.
            self.clean()
        except ValidationError as e:
            self._non_form_errors = self.error_class(e.error_list)
예제 #17
0
 def validate(self, password, user=None):
     if len(password) < self.min_length:
         raise ValidationError(
             ngettext(
                 "This password is too short. It must contain at least %(min_length)d character.",
                 "This password is too short. It must contain at least %(min_length)d characters.",
                 self.min_length),
             code='password_too_short',
             params={'min_length': self.min_length},
         )
예제 #18
0
    def __call__(self, value):
        # Check first if the scheme is valid
        scheme = value.split('://')[0].lower()
        if scheme not in self.schemes:
            raise ValidationError(self.message, code=self.code)

        # Then check full URL
        try:
            super().__call__(value)
        except ValidationError as e:
            # Trivial case failed. Try for possible IDN domain
            if value:
                try:
                    scheme, netloc, path, query, fragment = urlsplit(value)
                except ValueError:  # for example, "Invalid IPv6 URL"
                    raise ValidationError(self.message, code=self.code)
                try:
                    netloc = netloc.encode('idna').decode(
                        'ascii')  # IDN -> ACE
                except UnicodeError:  # invalid domain part
                    raise e
                url = urlunsplit((scheme, netloc, path, query, fragment))
                super().__call__(url)
            else:
                raise
        else:
            # Now verify IPv6 in the netloc part
            host_match = re.search(r'^\[(.+)\](?::\d{2,5})?$',
                                   urlsplit(value).netloc)
            if host_match:
                potential_ip = host_match.groups()[0]
                try:
                    validate_ipv6_address(potential_ip)
                except ValidationError:
                    raise ValidationError(self.message, code=self.code)

        # The maximum length of a full host name is 253 characters per RFC 1034
        # section 3.1. It's defined to be 255 bytes or less, but this includes
        # one byte for the length of the name and one byte for the trailing dot
        # that's used to indicate absolute names in DNS.
        if len(urlsplit(value).netloc) > 253:
            raise ValidationError(self.message, code=self.code)
예제 #19
0
 def __call__(self, value):
     extension = Path(value.name).suffix[1:].lower()
     if self.allowed_extensions is not None and extension not in self.allowed_extensions:
         raise ValidationError(self.message,
                               code=self.code,
                               params={
                                   'extension':
                                   extension,
                                   'allowed_extensions':
                                   ', '.join(self.allowed_extensions)
                               })
예제 #20
0
 def to_python(self, value):
     value = super().to_python(value)
     if value in self.empty_values:
         return None
     if not isinstance(value, uuid.UUID):
         try:
             value = uuid.UUID(value)
         except ValueError:
             raise ValidationError(self.error_messages['invalid'],
                                   code='invalid')
     return value
예제 #21
0
    def __call__(self, value):
        if not value or '@' not in value:
            raise ValidationError(self.message, code=self.code)

        user_part, domain_part = value.rsplit('@', 1)

        if not self.user_regex.match(user_part):
            raise ValidationError(self.message, code=self.code)

        if (domain_part not in self.domain_whitelist
                and not self.validate_domain_part(domain_part)):
            # Try for possible IDN domain-part
            try:
                domain_part = domain_part.encode('idna').decode('ascii')
            except UnicodeError:
                pass
            else:
                if self.validate_domain_part(domain_part):
                    return
            raise ValidationError(self.message, code=self.code)
예제 #22
0
 def split_url(url):
     """
     Return a list of url parts via urlparse.urlsplit(), or raise
     ValidationError for some malformed URLs.
     """
     try:
         return list(urlsplit(url))
     except ValueError:
         # urlparse.urlsplit can raise a ValueError with some
         # misformatted URLs.
         raise ValidationError(self.error_messages['invalid'],
                               code='invalid')
예제 #23
0
    def __call__(self, value):
        digit_tuple, exponent = value.as_tuple()[1:]
        if exponent in {'F', 'n', 'N'}:
            raise ValidationError(self.messages['invalid'])
        if exponent >= 0:
            # A positive exponent adds that many trailing zeros.
            digits = len(digit_tuple) + exponent
            decimals = 0
        else:
            # If the absolute value of the negative exponent is larger than the
            # number of digits, then it's the same as the number of digits,
            # because it'll consume all of the digits in digit_tuple and then
            # add abs(exponent) - len(digit_tuple) leading zeros after the
            # decimal point.
            if abs(exponent) > len(digit_tuple):
                digits = decimals = abs(exponent)
            else:
                digits = len(digit_tuple)
                decimals = abs(exponent)
        whole_digits = digits - decimals

        if self.max_digits is not None and digits > self.max_digits:
            raise ValidationError(
                self.messages['max_digits'],
                code='max_digits',
                params={'max': self.max_digits},
            )
        if self.decimal_places is not None and decimals > self.decimal_places:
            raise ValidationError(
                self.messages['max_decimal_places'],
                code='max_decimal_places',
                params={'max': self.decimal_places},
            )
        if (self.max_digits is not None and self.decimal_places is not None
                and whole_digits > (self.max_digits - self.decimal_places)):
            raise ValidationError(
                self.messages['max_whole_digits'],
                code='max_whole_digits',
                params={'max': (self.max_digits - self.decimal_places)},
            )
예제 #24
0
def _simple_domain_name_validator(value):
    """
    Validate that the given value contains no whitespaces to prevent common
    typos.
    """
    if not value:
        return
    checks = ((s in value) for s in string.whitespace)
    if any(checks):
        raise ValidationError(
            _("The domain name cannot contain any spaces or tabs."),
            code='invalid',
        )
예제 #25
0
 def run_validators(self, value):
     if value in self.empty_values:
         return
     errors = []
     for v in self.validators:
         try:
             v(value)
         except ValidationError as e:
             if hasattr(e, 'code') and e.code in self.error_messages:
                 e.message = self.error_messages[e.code]
             errors.extend(e.error_list)
     if errors:
         raise ValidationError(errors)
예제 #26
0
    def to_python(self, value):
        if not value:
            return {}
        if not isinstance(value, dict):
            try:
                value = json.loads(value)
            except json.JSONDecodeError:
                raise ValidationError(
                    self.error_messages['invalid_json'],
                    code='invalid_json',
                )

        if not isinstance(value, dict):
            raise ValidationError(
                self.error_messages['invalid_format'],
                code='invalid_format',
            )

        # Cast everything to strings for ease.
        for key, val in value.items():
            if val is not None:
                val = str(val)
            value[key] = val
        return value
예제 #27
0
    def to_python(self, data):
        if data in self.empty_values:
            return None

        # UploadedFile objects should have name and size attributes.
        try:
            file_name = data.name
            file_size = data.size
        except AttributeError:
            raise ValidationError(self.error_messages['invalid'],
                                  code='invalid')

        if self.max_length is not None and len(file_name) > self.max_length:
            params = {'max': self.max_length, 'length': len(file_name)}
            raise ValidationError(self.error_messages['max_length'],
                                  code='max_length',
                                  params=params)
        if not file_name:
            raise ValidationError(self.error_messages['invalid'],
                                  code='invalid')
        if not self.allow_empty_file and not file_size:
            raise ValidationError(self.error_messages['empty'], code='empty')

        return data
예제 #28
0
 def _coerce(self, value):
     """
     Validate that the value can be coerced to the right type (if not empty).
     """
     if value == self.empty_value or value in self.empty_values:
         return self.empty_value
     try:
         value = self.coerce(value)
     except (ValueError, TypeError, ValidationError):
         raise ValidationError(
             self.error_messages['invalid_choice'],
             code='invalid_choice',
             params={'value': value},
         )
     return value
예제 #29
0
 def run_validators(self, value):
     super().run_validators(value)
     errors = []
     for index, item in enumerate(value):
         try:
             self.base_field.run_validators(item)
         except ValidationError as error:
             errors.append(
                 prefix_validation_error(
                     error,
                     prefix=self.error_messages['item_invalid'],
                     code='item_invalid',
                     params={'nth': index + 1},
                 ))
     if errors:
         raise ValidationError(errors)
예제 #30
0
 def to_python(self, value):
     """
     Validate that float() can be called on the input. Return the result
     of float() or None for empty values.
     """
     value = super(IntegerField, self).to_python(value)
     if value in self.empty_values:
         return None
     if self.localize:
         value = formats.sanitize_separators(value)
     try:
         value = float(value)
     except (ValueError, TypeError):
         raise ValidationError(self.error_messages['invalid'],
                               code='invalid')
     return value