コード例 #1
0
    def get_page_number(self):
        """
        获得页码
        :return:
        """
        page_number = self.request_handler.get_query_argument(self.page_query_param, 1)
        num_pages = self.paginator.num_pages

        if page_number in self.first_page_strings:
            page_number = 1

        if page_number in self.last_page_strings:
            page_number = num_pages

        try:
            page_number = int(page_number)
        except (TypeError, ValueError):
            raise PaginationError(detail=_("The page number must be a number or page string"))

        if page_number < 1:
            if self.allow_first_page:
                page_number = 1
            else:
                raise PaginationError(detail=_("The page number must be greater than 1"))

        if page_number > num_pages:
            if self.allow_last_page:
                page_number = num_pages

            if not self.allow_empty_page:
                raise PaginationError(detail=_("The page number exceeds the total page number"))

        return page_number
コード例 #2
0
class ListField(Field):
    """
    列表字段
    """
    child = _UnvalidatedField()
    initial = []
    default_error_messages = {
        'not_a_list':
        _('Expected a list of items but got type "%(input_type)s"'),
        'empty': _('This list may not be empty'),
        'min_length':
        _('Ensure this field has at least {min_length} elements'),
        'max_length':
        _('Ensure this field has no more than {max_length} elements')
    }

    def __init__(self, child=None, *args, **kwargs):
        self.child = copy.deepcopy(self.child) if child is None else child
        assert not inspect.isclass(
            self.child), '`child` has not been instantiated.'
        self.allow_empty = kwargs.pop('allow_empty', True)
        self.max_length = kwargs.pop('max_length', None)
        self.min_length = kwargs.pop('min_length', None)
        self.child.source = None

        super(ListField, self).__init__(*args, **kwargs)
        self.child.bind(field_name='', parent=self)

        if self.max_length is not None:
            message = self.error_messages['max_length'].format(
                max_length=self.max_length)
            self.validators.append(
                validators.MaxLengthValidator(self.max_length,
                                              message=message))
        if self.min_length is not None:
            message = self.error_messages['min_length'].format(
                min_length=self.min_length)
            self.validators.append(
                validators.MinLengthValidator(self.min_length,
                                              message=message))

    def validate(self, value):
        pass

    async def clean(self, value):
        if value is empty:
            value = []
        elif isinstance(value, type('')) or isinstance(value, collections.Mapping)\
                or not hasattr(value, '__iter__'):
            raise ValidationError(self.error_messages['not_a_list'],
                                  code='not_a_list',
                                  params=dict(input_type=type(value).__name__))

        if not self.allow_empty and len(value) == 0:
            raise ValidationError(self.error_messages['empty'], code='empty')
        return [await self.child.clean(item) for item in value]
コード例 #3
0
class DecimalValidator(object):
    """
    Validate that the input does not exceed the maximum number of digits
    expected, otherwise raise ValidationError.
    """
    messages = {
        'max_digits':
        _('Ensure that there are no more than %(max)s digit in total'),
        'max_decimal_places':
        _('Ensure that there are no more than %(max)s decimal place'),
        'max_whole_digits':
        _('Ensure that there are no more than %(max)s digit before the decimal point'
          ),
    }

    def __init__(self, max_digits, decimal_places):
        self.max_digits = max_digits
        self.decimal_places = decimal_places

    def __call__(self, value):
        digit_tuple, exponent = value.as_tuple()[1:]
        decimals = abs(exponent)
        # digit_tuple doesn't include any leading zeros.
        digits = len(digit_tuple)
        if decimals > digits:
            # We have leading zeros up to or past the decimal point. Count
            # everything past the decimal point as a digit. We do not count
            # 0 before the decimal point as a digit since that would mean
            # we would not allow max_digits = decimal_places.
            digits = decimals
        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)},
            )

    def __eq__(self, other):
        return (isinstance(other, self.__class__)
                and self.max_digits == other.max_digits
                and self.decimal_places == other.decimal_places)
コード例 #4
0
class FormModelField(Field):
    default_error_messages = {
        'type_error':
        _('Expected a list or dictionary of items but got type "%(input_type)s"'
          ),
        'empty':
        _('This value not be empty')
    }

    def __init__(self, form, many=True, allow_empty=False, *args, **kwargs):
        self.form_class = formset_factory(form)
        self.many = many
        self.allow_empty = allow_empty
        super(FormModelField, self).__init__(*args, **kwargs)

    def validate(self, value):
        pass

    async def clean(self, value):
        if self.required and value is empty:
            raise ValidationError(self.error_messages['required'],
                                  code='required')

        if value is empty:
            value = [] if self.many else {}

        elif not isinstance(value, (dict, list)):
            raise ValidationError(self.error_messages['type_error'],
                                  code='type_error',
                                  params=dict(input_type=type(value).__name__))

        form_data_size = len(value)
        if self.allow_empty and form_data_size == 0:
            return [] if self.many else {}

        # elif form_data_size == 0:
        #     raise ValidationError(self.error_messages['empty'], code='empty')

        if isinstance(value, dict):
            value = [value]

        form_cls = self.form_class(data=value)
        is_valid = await form_cls.is_valid()

        if is_valid:
            result = await form_cls.cleaned_data
            return result if self.many else result[0]

        errors = await form_cls.errors
        raise ValidationError(errors)
コード例 #5
0
    def pre_handle_exception(self, exc):
        """
        预处理各种异常返回结构
        返回Result对象
        :param exc:
        :return:Result
        """
        if isinstance(exc,
                      (exceptions.APIException, exceptions.ValidationError)):
            error_response = self.write_response(data={
                settings.NON_FIELD_ERRORS: {
                    "message": exc.detail,
                    "code": exc.code
                }
            },
                                                 status_code=exc.status_code)

        elif isinstance(exc, HTTPError):
            status_code = exc.status_code
            http_code_detail = status.HTTP_CODES.get(status_code, None)
            error_response = self.write_response(data={
                settings.NON_FIELD_ERRORS: {
                    "message":
                    http_code_detail.description
                    if http_code_detail else _("Internal Server Error"),
                    "code":
                    "Error"
                }
            },
                                                 status_code=exc.status_code)

        elif isinstance(exc, IntegrityError):
            error_detail = ErrorDetail(
                _('Insert failed, the reason may be foreign key constraints'),
                code="ForeignError")
            error_response = self.write_response(
                data={settings.NON_FIELD_ERRORS: error_detail},
                status_code=status.HTTP_400_BAD_REQUEST)
        else:
            error_response = self.write_response(
                data={
                    settings.NON_FIELD_ERRORS: {
                        "message": _("Internal Server Error"),
                        "code": "Error"
                    }
                },
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)

        return error_response
コード例 #6
0
class BooleanField(Field):
    default_error_messages = {
        'invalid': _('"%s" is not a valid boolean')
    }
    initial = False
    TRUE_VALUES = {
        't', 'T',
        'y', 'Y', 'yes', 'YES',
        'true', 'True', 'TRUE',
        'on', 'On', 'ON',
        '1', 1,
        True
    }
    FALSE_VALUES = {
        'f', 'F',
        'n', 'N', 'no', 'NO',
        'false', 'False', 'FALSE',
        'off', 'Off', 'OFF',
        '0', 0, 0.0,
        False
    }

    def to_python(self, value):
        """Returns a Python boolean object."""
        if value in self.TRUE_VALUES:
            return True
        elif value in self.FALSE_VALUES:
            return False
        else:
            raise ValidationError(self.error_messages['invalid'], code='invalid', params=value)
コード例 #7
0
class DecimalField(IntegerField):
    default_error_messages = {
        'invalid': _('Enter a number'),
    }

    def __init__(self, max_value=None, min_value=None, max_digits=None, decimal_places=None, *args, **kwargs):
        self.max_digits, self.decimal_places = max_digits, decimal_places
        super(DecimalField, self).__init__(max_value, min_value, *args, **kwargs)
        self.validators.append(validators.DecimalValidator(max_digits, decimal_places))

    def to_python(self, value):
        """
        Validates that the input is a decimal number. Returns a Decimal
        instance. Returns None for empty values. Ensures that there are no more
        than max_digits in the number, and no more than decimal_places digits
        after the decimal point.
        """
        if value in self.empty_values:
            return None
        value = force_text(value).strip()
        try:
            value = Decimal(value)
        except DecimalException:
            raise ValidationError(self.error_messages['invalid'], code='invalid')
        return value

    def validate(self, value):
        super(DecimalField, self).validate(value)
        if value in self.empty_values:
            return
        # Check for NaN, Inf and -Inf values. We can't compare directly for NaN,
        # since it is never equal to itself. However, NaN is the only value that
        # isn't equal to itself, so we can use this to identify NaN
        if value != value or value == Decimal("Inf") or value == Decimal("-Inf"):
            raise ValidationError(self.error_messages['invalid'], code='invalid')
コード例 #8
0
class CharField(Field):
    """
    字符串类型
    """
    default_error_messages = {
        'invalid':  _('Not a valid string'),
    }
    initial = ''

    def __init__(self, max_length=None, min_length=None, strip=True, empty_value='', *args, **kwargs):
        self.max_length = max_length
        self.min_length = min_length
        self.strip = strip
        self.empty_value = empty_value
        super(CharField, self).__init__(*args, **kwargs)

        if min_length is not None:
            self.validators.append(validators.MinLengthValidator(int(min_length)))
        if max_length is not None:
            self.validators.append(validators.MaxLengthValidator(int(max_length)))

    def to_python(self, value):
        """
        Returns a Unicode object
        :param value:
        :return:
        """
        if value in self.empty_values:
            return self.empty_value
        value = force_text(value)
        if self.strip:
            value = value.strip()
        return value
コード例 #9
0
    async def get_object(self):
        """
        查询单一对象,如果为空抛出404
        """
        try:
            queryset = await self.filter_queryset(self.get_queryset())
        except SkipFilterError:
            error_msg = self.error_msg_404 if self.error_msg_404 else {}
            raise exceptions.APIException(
                status_code=404,
                detail=error_msg.get("message",
                                     _("Resource data does not exist")),
                code=error_msg.get("code", "ResourceNotExist"))

        queryset = queryset.naive()
        lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
        path_kwargs = self.path_kwargs or self.request_data
        assert lookup_url_kwarg in path_kwargs, (
            'Expected view %s to be called with a URL keyword argument '
            'named "%s". Fix your URL conf, or set the `.lookup_field` '
            'attribute on the view correctly.' %
            (self.__class__.__name__, lookup_url_kwarg))

        filter_kwargs = {self.lookup_field: path_kwargs[lookup_url_kwarg]}
        obj = await self.get_object_or_404(queryset, **filter_kwargs)

        return obj
コード例 #10
0
class DictField(Field):
    """
    字典字段
    """
    child = _UnvalidatedField()
    initial = {}
    default_error_messages = {
        'not_a_dict':
        _('Expected a dictionary of items but got type "%(input_type)s"')
    }

    def __init__(self, *args, **kwargs):
        self.child = kwargs.pop('child', copy.deepcopy(self.child))
        assert not inspect.isclass(
            self.child), '`child` has not been instantiated.'
        self.child.source = None
        super(DictField, self).__init__(*args, **kwargs)
        self.child.bind(field_name='', parent=self)

    async def clean(self, data):
        if not isinstance(data, dict):
            raise ValidationError(self.error_messages['not_a_dict'],
                                  code='not_a_dict',
                                  params=dict(input_type=type(data).__name__))

        return {
            str(key): await self.child.clean(value)
            for key, value in data.items()
        }
コード例 #11
0
class MinValueValidator(BaseValidator):
    message = _(
        'Ensure this value is greater than or equal to %(limit_value)s')
    code = 'min_value'

    def compare(self, a, b):
        return a < b
コード例 #12
0
class PaginationError(APIException):
    """
    分页异常
    """
    status_code = status.HTTP_400_BAD_REQUEST
    default_detail = _('Invalid page')
    default_code = 'page_error'
コード例 #13
0
class FileExtensionValidator(object):
    message = _("File extension '%(extension)s' is not allowed. "
                "Allowed extensions are: '%(allowed_extensions)s'.")
    code = 'invalid_extension'

    def __init__(self, allowed_extensions=None, message=None, code=None):
        self.allowed_extensions = allowed_extensions
        if message is not None:
            self.message = message
        if code is not None:
            self.code = code

    def __call__(self, value):
        extension = os.path.splitext(value.name)[1][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)
                                  })

    def __eq__(self, other):
        return (isinstance(other, self.__class__)
                and self.allowed_extensions == other.allowed_extensions
                and self.message == other.message and self.code == other.code)
コード例 #14
0
class BaseValidator(object):
    message = _('Ensure this value is %(limit_value)s (it is %(show_value)s)')
    code = 'limit_value'

    def __init__(self, limit_value, message=None):
        self.limit_value = limit_value
        if message:
            self.message = message

    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)

    def __eq__(self, other):
        return (isinstance(other, self.__class__)
                and self.limit_value == other.limit_value
                and self.message == other.message and self.code == other.code)

    def compare(self, a, b):
        return a is not b

    def clean(self, x):
        return x
コード例 #15
0
class IntegerField(Field):
    default_error_messages = {
        'invalid': _('Enter a whole number'),
    }
    re_decimal = re.compile(r'\.0*\s*$')

    def __init__(self, max_value=None, min_value=None, *args, **kwargs):
        self.max_value, self.min_value = max_value, min_value
        super(IntegerField, self).__init__(*args, **kwargs)

        if max_value is not None:
            self.validators.append(validators.MaxValueValidator(max_value))
        if min_value is not None:
            self.validators.append(validators.MinValueValidator(min_value))

    def to_python(self, value):
        """
        Validates that int() can be called on the input. Returns the result
        of int(). Returns None for empty values.
        """
        value = super(IntegerField, self).to_python(value)
        if value in self.empty_values:
            return None
        try:
            value = int(self.re_decimal.sub('', force_text(value)))
        except (ValueError, TypeError):
            raise ValidationError(self.error_messages['invalid'],
                                  code='invalid')
        return value
コード例 #16
0
class FloatField(IntegerField):
    default_error_messages = {
        'invalid': _('Enter a number'),
    }

    def to_python(self, value):
        """
        Validates that float() can be called on the input. Returns the result
        of float(). Returns None for empty values.
        """
        value = super(IntegerField, self).to_python(value)
        if value in self.empty_values:
            return None
        try:
            value = float(value)
        except (ValueError, TypeError):
            raise ValidationError(self.error_messages['invalid'],
                                  code='invalid')
        return value

    def validate(self, value):
        super(FloatField, self).validate(value)

        # Check for NaN (which is the only thing not equal to itself) and +/- infinity
        if value != value or value in (Decimal('Inf'), Decimal('-Inf')):
            raise ValidationError(self.error_messages['invalid'],
                                  code='invalid')

        return value
コード例 #17
0
class ParseError(APIException):
    """
    解析异常
    """
    status_code = status.HTTP_400_BAD_REQUEST
    default_detail = _('Malformed request')
    default_code = 'parse_error'
コード例 #18
0
class APIException(Exception):
    status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
    default_detail = _('A server error occurred')
    default_code = 'error'

    def __init__(self, detail=None, code=None, status_code=None):
        if detail is None:
            detail = self.default_detail

        if code is None:
            self.code = self.default_code

        if status_code is not None:
            self.status_code = status_code

        self.detail = _get_error_details(detail, code)

    def __str__(self):
        return self.detail

    def get_codes(self):
        """
        Return only the code part of the error details.

        Eg. {"name": ["required"]}
        """
        return _get_codes(self.detail)

    def get_full_details(self):
        """
        Return both the message & code parts of the error details.

        Eg. {"name": [{"message": "This field is required.", "code": "required"}]}
        """
        return get_full_details(self.detail)
コード例 #19
0
    async def prepare(self):
        method = self.request.method.lower()
        content_type = self.request.headers.get("Content-Type", "").lower()
        self.request_data = self._parse_query_arguments()
        if not content_type or method == b"get":
            if self.path_kwargs:
                self.request_data.update(self.path_kwargs)
            self.request.data = self.request_data
            return

        parser = self.__select_parser(content_type)
        if not parser:
            error_detail = _(
                'Unsupported media type `%s` in request') % content_type
            raise APIException(
                detail=error_detail,
                code="MediaTypeError",
                status_code=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE)

        parse_data = await parser.parse(self.request)
        if parse_data:
            if isinstance(parse_data, dict):
                self.request_data.update(parse_data)
            else:
                self.request_data = parse_data

        self.request.data = self.request_data
コード例 #20
0
class PasswordValidator(object):
    """
    密码是否合法
    """
    message = {
        "number":
        _("Enter a valid 6-digit password"),
        "normal":
        _("Enter a valid 6-18-digit alphanumeric password"),
        "high":
        _("Enter a 6-18 bit must contain any combination of "
          "upper and lower case letters, numbers, symbols password")
    }
    code = 'invalid'

    def __init__(self, level="number", message=None, code=None, regex=None):
        self.level = level

        if message is not None:
            self.message = message

        if code is not None:
            self.code = code

        self.password_regex = lazy_re_compile(regex, flags=re.IGNORECASE) \
            if regex is not None else None

        if self.level == "number":
            self.password_regex = lazy_re_compile(r"^\d{6}$",
                                                  flags=re.IGNORECASE)
        elif self.level == "normal":
            self.password_regex = lazy_re_compile(
                r"^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,18}$",
                flags=re.IGNORECASE)
        elif self.level == "high":
            re_str = r"^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z\u4e00-\u9fa5\s)])+$)" \
                     r"([^(0-9a-zA-Z\u4e00-\u9fa5\s)]|[a-z]|[A-Z]|[0-9]){6,18}$"
            self.password_regex = lazy_re_compile(re_str, flags=re.IGNORECASE)

    def __call__(self, value):
        if self.level == "any" or self.password_regex is None:
            return

        valid = self.password_regex.match(value)

        if not valid:
            raise ValidationError(self.message[self.level], code=self.code)
コード例 #21
0
class UniqueValidator(object):
    """
    model模型字段设置了unique=True,进行唯一索引检查
    """

    message = _("This field (`%(field_name)s`) value must be unique")

    def __init__(self, queryset, message=None, lookup='exact'):
        self.queryset = queryset
        self.serializer_field = None
        self.message = message or self.message
        self.lookup = lookup

    def set_context(self, serializer_field):
        """
        This hook is called by the serializer instance,
        prior to the validation call being made.
        """
        # Determine the underlying model field name. This may not be the
        # same as the serializer field name if `source=<>` is set.
        self.field_name = serializer_field.source_attrs[-1]
        # Determine the existing instance, if this is an update operation.
        self.instance = getattr(serializer_field.parent, 'instance', None)

    def filter_queryset(self, value, queryset):
        """
        Filter the queryset to all instances matching the given attribute.
        """
        filter_kwargs = {'%s__%s' % (self.field_name, self.lookup): value}
        return qs_filter(queryset, **filter_kwargs)

    def exclude_current_instance(self, queryset):
        """
        If an instance is being updated, then do not include
        that instance itself as a uniqueness conflict.
        """
        if self.instance is not None:
            pk_field = self.instance._meta.primary_key
            return queryset.exclude(**{pk_field.name: getattr(self.instance, pk_field.name)})
        return queryset

    @asyncio.coroutine
    def __call__(self, value):
        queryset = self.queryset
        queryset = self.filter_queryset(value, queryset)
        queryset = self.exclude_current_instance(queryset)
        if (yield from qs_exists(queryset)):
            raise ValidationError(
                self.message, code='unique',
                params={"field_name": self.field_name}
            )

    def __repr__(self):
        return '<%s(queryset=%s)>' % (
            self.__class__.__name__,
            str(self.queryset),
        )
コード例 #22
0
class MaxLengthValidator(BaseValidator):
    message = _(
        'Ensure this value has at most %(limit_value)d character (it has %(show_value)d)'
    )
    code = 'max_length'

    def compare(self, a, b):
        return a > b

    def clean(self, x):
        return len(x)
コード例 #23
0
    async def full_clean(self):
        """
        Cleans all of self.data and populates self._errors and
        self._non_form_errors.
        """
        self._errors = {}

        if not self.is_bound:
            return

        if not isinstance(self.data, list):
            raise ValidationError(
                detail=_("The form data format must be a list structure, not a %s structure."),
                code='FormDataFormatError',
                params=type(self.data).__name__
            )
        for i in range(0, self.total_form_count):
            form = self.forms[i]
            form_error = await form.part_errors
            if form_error:
                for k, v in form_error.items():
                    self._errors["%s-%d" % (k, i+1)] = v

        try:
            if self.max_num is not None and self.total_form_count > self.max_num:
                raise ValidationError(
                    detail=_("Please submit %d or fewer forms"),
                    code='too_many_forms',
                    params=self.max_num
                )
            if self.min_num is not None and self.total_form_count < self.min_num:
                raise ValidationError(
                    detail=_("Please submit %d or more forms"),
                    code='too_few_forms',
                    params=self.min_num
                )

            self.clean()
        except ValidationError as e:
            self._errors[settings.NON_FIELD_ERRORS] = e.detail
コード例 #24
0
class MultipleChoiceField(ChoiceField):
    default_error_messages = {
        'invalid_choice': _('"%(input)s" is not a valid choice'),
        'invalid_list': _('Enter a list of values'),
    }

    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 [force_text(val) for val in value]

    def validate(self, value):
        """
        Validates 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={'input': val},
                )

    def has_changed(self, initial, data):
        if initial is None:
            initial = []
        if data is None:
            data = []
        if len(initial) != len(data):
            return True
        initial_set = set(force_text(value) for value in initial)
        data_set = set(force_text(value) for value in data)
        return data_set != initial_set
コード例 #25
0
class IdentifierValidator(object):
    """
    手机号码或邮箱地址是否合法
    """

    message = {
        "both": _("Enter a valid phone number or email address"),
        "phone": _("Enter a valid phone number"),
        "email": _("Enter a valid email address")
    }
    code = 'invalid'

    def __init__(self, protocol="both", message=None, code=None):
        self.protocol = protocol
        # assert isinstance(message, (type(None), dict)), "message值必须为字典类型"
        if message is not None:
            self.message = message

        if code is not None:
            self.code = code

    def __call__(self, value):
        if not value:
            raise ValidationError(self.message[self.protocol], code=self.code)

        if self.protocol == "both":
            if "@" in value:
                validate_email(value)
            elif value.isdigit():
                validate_phone(value)
            else:
                raise ValidationError(self.message[self.protocol],
                                      code=self.code)

        elif self.protocol == "email":
            validate_email(value)

        elif self.protocol == "phone":
            validate_phone(value)
コード例 #26
0
class ImageField(FileField):
    default_error_messages = {
        'invalid_image':
        _("Upload a valid image. The file you uploaded was either not an "
          "image or a corrupted image"),
    }

    def to_python(self, data):
        """
        Checks that the file-upload field data contains a valid image (GIF, JPG,
        PNG, possibly others -- whatever the Python Imaging Library supports).
        """
        f = super(ImageField, self).to_python(data)
        if f is None:
            return None

        from PIL import Image

        # We need to get a file object for Pillow. We might have a path or we might
        # have to read the data into memory.
        if hasattr(data, 'temporary_file_path'):
            file = data.temporary_file_path()
        else:
            if hasattr(data, 'read'):
                file = BytesIO(data.read())
            else:
                file = BytesIO(data['content'])

        try:
            # load() could spot a truncated JPEG, but it loads the entire
            # image in memory, which is a DoS vector. See #3848 and #18520.
            image = Image.open(file)
            # verify() must be called immediately after the constructor.
            image.verify()

            # Annotating so subclasses can reuse it for their own validation
            f.image = image
            # Pillow doesn't detect the MIME type of all formats. In those
            # cases, content_type will be None.
            f.content_type = Image.MIME.get(image.format)
        except Exception:
            raise ValidationError(self.error_messages['invalid_image'],
                                  code='invalid_image')

        if hasattr(f, 'seek') and callable(f.seek):
            f.seek(0)

        return f
コード例 #27
0
class RegexValidator(object):
    regex = ''
    message = _('Enter a valid value')
    code = 'invalid'
    inverse_match = False
    flags = 0

    def __init__(self,
                 regex=None,
                 message=None,
                 code=None,
                 inverse_match=None,
                 flags=None):
        if regex is not None:
            self.regex = regex
        if message is not None:
            self.message = message
        if code is not None:
            self.code = code
        if inverse_match is not None:
            self.inverse_match = inverse_match
        if flags is not None:
            self.flags = flags
        if self.flags and not isinstance(self.regex, str):
            raise TypeError(
                "If the flags are set, regex must be a regular expression string."
            )

        self.regex = lazy_re_compile(self.regex, self.flags)

    def __call__(self, value):
        """
        Validates that the input matches the regular expression
        if inverse_match is False, otherwise raises ValidationError.
        """
        if not (self.inverse_match is not bool(
                self.regex.search(force_text(value)))):
            raise ValidationError(self.message, code=self.code)

    def __eq__(self, other):
        return (isinstance(other, RegexValidator)
                and self.regex.pattern == other.regex.pattern
                and self.regex.flags == other.regex.flags
                and (self.message == other.message)
                and (self.code == other.code)
                and (self.inverse_match == other.inverse_match))
コード例 #28
0
class TimeField(BaseTemporalField):
    input_formats = get_format_lazy('TIME_INPUT_FORMATS')
    default_error_messages = {'invalid': _('Enter a valid time')}

    def to_python(self, value):
        """
        Validates that the input can be converted to a time. Returns a Python
        datetime.time object.
        """
        if value in self.empty_values:
            return None
        if isinstance(value, datetime.time):
            return value
        return super(TimeField, self).to_python(value)

    def strptime(self, value, format):
        return datetime.datetime.strptime(force_text(value), format).time()
コード例 #29
0
class ValidationError(APIException):
    status_code = status.HTTP_400_BAD_REQUEST
    default_detail = _('Invalid input.')
    default_code = 'invalid'

    def __init__(self, detail=None, code=None, params=None, field=None):
        if detail is None:
            detail = self.default_detail
        self.code = self.default_code if code is None else code
        self.field = field
        if params is not None:
            detail %= params

        self.detail = _get_error_details(detail, self.code)

    def __str__(self):
        return str(self.detail)
コード例 #30
0
class PasswordField(CharField):
    """
    密码字段类型
    """
    default_error_messages = {
        'invalid': _('Please enter a valid password')
    }

    def __init__(self, protection='default', level="high", *args, **kwargs):
        """
        :param protection: 密码加密方式
                           default 默认,取settings PASSWORD_HASHERS的第1个
                           pbkdf2_sha256
                           pbkdf2_sha1
                           argon2
                           bcrypt_sha256
                           bcrypt

        :param level: 密码加密级别
                       any      任何版本,不限制
                       number   数字版本,6位数字密码
                       normal   普通版本,6-18位英文数字混合密码
                       high     增强版本,6-18位必须包含大小写字母/数字/符号任意两者组合密码
        :param args:
        :param kwargs:
        """
        if protection != "default":
            assert protection in hashers.get_hashers_by_algorithm().keys(), "protection不正确"
        assert level in ('any', "number", "normal", "high"), "level不正确"
        self.protection = protection.lower()
        self.level = level.lower()
        super(PasswordField, self).__init__(*args, **kwargs)
        self.validators.append(validators.PasswordValidator(self.level))

    async def clean(self, value):
        """
        Validates the given value and returns its "cleaned" value as an
        appropriate Python object.

        Raises ValidationError for any errors.
        """
        value = self.to_python(value)
        self.validate(value)
        await self.run_validators(value)
        return hashers.make_password(password=value, hasher=self.protection)