DRF: 如何使用Serializers/Fields/Validators

DRF:序列化和反序列化

  • 请求过来的json需要转换为python中的对象
  • python中的对象需要转换为json作为响应

场景:

  • 请求(json/form) ——> 反序列化 ——> python可以操作的对象
  • orm: sql执行结果->queryset(python对象)
  • queryset(py) --> 序列化 --> json

本文目的: 探索一下drf序列化的流程.

  • Validator(验证器): rest_framework.validators.py * *
  • Field(字段): rest_framework.fields.py *
  • Serializer(序列化类): rest_framework.serializers.py
    • 验证字段合法
    • 验证业务逻辑合法
  • Serializer 和 Field之前调用关系
  • 自定义Field

1.Validator

2.Field

  • Field(object): 字段类
  • BooleanField(Field)
  • CharField(Field)
  • ChoiceField(Field)
  • DateField(Field)
  • DateTimeField(Field)
  • DecimalField(Field)
  • DictField(Field)
  • DurationField(Field)
  • EmailField(CharField): 邮件字段
  • RegexField(CharField): 正则
  • FileField(Field): 文件
  • FilePathField(ChoiceField)
  • FloatField(Field)
  • HiddenField(Field)
  • ImageField(FileField)
  • IntegerField(Field)
  • IPAddressField(CharField)
  • JSONField(Field)
  • ListField(Field)
  • ModelField(Field)
  • MultipleChoiceField(ChoiceField)
  • NullBooleanField(Field) *
  • ReadOnlyField(Field):
    • init: kwargs['read_only'] = True
    • to_representation: py -> response

1.1 分析NullBooleanField

  • init: 设置allow_null为True
  • to_internal_value: 把value转为py对象True,False,None
  • to_representation:
class NullBooleanField(Field):
    default_error_messages = {
        'invalid': _('"{input}" is not a valid boolean.')
    }
    initial = None
    TRUE_VALUES = {'t', 'T', 'true', 'True', 'TRUE', '1', 1, True}
    FALSE_VALUES = {'f', 'F', 'false', 'False', 'FALSE', '0', 0, 0.0, False}
    NULL_VALUES = {'n', 'N', 'null', 'Null', 'NULL', '', None}

    def __init__(self, **kwargs):
        assert 'allow_null' not in kwargs, '`allow_null` is not a valid option.'
        kwargs['allow_null'] = True
        super(NullBooleanField, self).__init__(**kwargs)

    def to_internal_value(self, data):
        if data in self.TRUE_VALUES:
            return True
        elif data in self.FALSE_VALUES:
            return False
        elif data in self.NULL_VALUES:
            return None
        self.fail('invalid', input=data)

    def to_representation(self, value):
        if value in self.NULL_VALUES:
            return None
        if value in self.TRUE_VALUES:
            return True
        elif value in self.FALSE_VALUES:
            return False
        return bool(value)

3.Serializer

app.serializers.py

class TestModelSerializer(BaseSerializer):
    """
    TestModel Serializer
    """
    currentPage = IntegerWithNullField(
        required=False,
        help_text=u'当前页数(从1开始取值),当method=page 时必传')

    def validate(self, attrs):
        # 业务逻辑是否合法
        return attrs

views.py

class APIPullProductInfoSView(AllowAnyView):
    """
    APIPullProductInfoSView

    """
    serializer_class = APIPullProductInfoSerializer

    def get(self, request):
        """
        ---

        """
        logger.info("request.data:{}".format(request.data))
        serializer = self.get_serializer(data=request.data)
        if not serializer.is_valid():
            logger.debug('serializer err:{}'.format(serializer.errors))
            return Response(
                serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        validated_data = serializer.validated_data
        return Response(validated_data)

1.Serializer触发流程

is_valid调用前:

  • new
  • init *
  • many_init

is_valid 调用时:

  • run_validation
  • to_internal_value

is_valid调用后:

  • to_representation

rest_framework.serializers.BaseSerializer

class BaseSerializer(Field):
    def __init__(self, instance=None, data=empty, **kwargs):
        # 绑定model instantce
        self.instance = instance
        if data is not empty:
            # 绑定数据到initial_data
            self.initial_data = data
        self.partial = kwargs.pop('partial', False)
        self._context = kwargs.pop('context', {})
        kwargs.pop('many', None)
        super(BaseSerializer, self).__init__(**kwargs)

    def __new__(cls, *args, **kwargs):
        # We override this method in order to automagically create
        # `ListSerializer` classes instead when `many=True` is set.
        if kwargs.pop('many', False):
            return cls.many_init(*args, **kwargs)
        return super(BaseSerializer, cls).__new__(cls, *args, **kwargs)

    @classmethod
    def many_init(cls, *args, **kwargs):
        """
        This method implements the creation of a `ListSerializer` parent
        class when `many=True` is used. You can customize it if you need to
        control which keyword arguments are passed to the parent, and
        which are passed to the child.

        Note that we're over-cautious in passing most arguments to both parent
        and child classes in order to try to cover the general case. If you're
        overriding this method you'll probably want something much simpler, eg:

        @classmethod
        def many_init(cls, *args, **kwargs):
            kwargs['child'] = cls()
            return CustomListSerializer(*args, **kwargs)
        """
        allow_empty = kwargs.pop('allow_empty', None)
        child_serializer = cls(*args, **kwargs)
        list_kwargs = {
            'child': child_serializer,
        }
        if allow_empty is not None:
            list_kwargs['allow_empty'] = allow_empty
        list_kwargs.update({
            key: value for key, value in kwargs.items()
            if key in LIST_SERIALIZER_KWARGS
        })
        meta = getattr(cls, 'Meta', None)
        list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
        return list_serializer_class(*args, **list_kwargs)

    def to_internal_value(self, data):
        raise NotImplementedError('`to_internal_value()` must be implemented.')

    def to_representation(self, instance):
        raise NotImplementedError('`to_representation()` must be implemented.')

    def update(self, instance, validated_data):
        raise NotImplementedError('`update()` must be implemented.')

    def create(self, validated_data):
        raise NotImplementedError('`create()` must be implemented.')

    def save(self, **kwargs):
        validated_data = dict(
            list(self.validated_data.items()) +
            list(kwargs.items())
        )
        if self.instance is not None:
            self.instance = self.update(self.instance, validated_data)
            assert self.instance is not None, (
                '`update()` did not return an object instance.'
            )
        else:
            self.instance = self.create(validated_data)
            assert self.instance is not None, (
                '`create()` did not return an object instance.'
            )

        return self.instance

    def is_valid(self, raise_exception=False):
        # 调用者触发.
        if not hasattr(self, '_validated_data'):
            try:
                # 调用self.run_validation
                self._validated_data = self.run_validation(self.initial_data)
            except ValidationError as exc:
                self._validated_data = {}
                self._errors = exc.detail
            else:
                self._errors = {}
        if self._errors and raise_exception:
            raise ValidationError(self.errors)
        # 检查是否发生了验证异常
        return not bool(self._errors)

    @property
    def data(self):
        # 获取序列化器的数据
        if not hasattr(self, '_data'):
            if self.instance is not None and not getattr(self, '_errors', None):
                # 更新
                self._data = self.to_representation(self.instance)
            elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None):
                # 创建
                self._data = self.to_representation(self.validated_data)
            else:
                self._data = self.get_initial()
        return self._data

    @property
    def errors(self):
        # 获取错误...
        return self._errors

    @property
    def validated_data(self):
        # 获取验证后的数据...
        return self._validated_data

2.Serializer: 实现run_validation

  • run_validation: 运行序列化器的验证器
    • self.validate_empty_values(data)
    • value = self.to_internal_value(data):
      • 遍历序列化器类的所有字段: for field in fields:(该Field可以嵌套Serializer)
        • 先获取该Field的值,primitive_value = field.get_value(data)
        • 运行字段的验证器validated_value = field.run_validation(primitive_value)
          • (is_empty_value, data) = filed.validate_empty_values(data)
          • value = filed.to_internal_value(data)
          • self.run_validators(value)
            • 最大值验证
            • 最小值验证
            • 正则验证...
        • 自定义字段验证方法:validate_method: validate_field.field_name
          • validated_value = validate_method(validated_value)
    • self.run_validators(value): 检查该序列化器类中所有的字段都符合要求
    • value = self.validate(value): 整个输入对象,是否符合要求.serializer.validated_data
@six.add_metaclass(SerializerMetaclass)
class Serializer(BaseSerializer):
    default_error_messages = {
        'invalid': _('Invalid data. Expected a dictionary, but got {datatype}.')
    }

    def is_valid(self, raise_exception=False):
        if not hasattr(self, '_validated_data'):
            try:
                self._validated_data = self.run_validation(self.initial_data)
            except ValidationError as exc:
                self._validated_data = {}
                self._errors = exc.detail
            else:
                self._errors = {}
        if self._errors and raise_exception:
            raise ValidationError(self.errors)
        return not bool(self._errors)

    def run_validation(self, data=empty):
        # 1.验证是否为空
        (is_empty_value, data) = self.validate_empty_values(data)
        if is_empty_value:
            return data
        # 2.输入转为python对象. 对所有field进行验证,失败抛出异常
        value = self.to_internal_value(data)
        try:
            # 3.
            self.run_validators(value)
            # 4. 序列化的validate方法, 所有filed已经验证完毕.
            value = self.validate(value)
            assert value is not None, '.validate() should return the validated data'
        except (ValidationError, DjangoValidationError) as exc:
            raise ValidationError(detail=as_serializer_error(exc))
        return value

    def to_internal_value(self, data):
        if not isinstance(data, dict):
            message = self.error_messages['invalid'].format(
                datatype=type(data).__name__
            )
            raise ValidationError({
                api_settings.NON_FIELD_ERRORS_KEY: [message]
            }, code='invalid')

        ret = OrderedDict()
        errors = OrderedDict()
        fields = self._writable_fields
        # fields: 当前序列化类的可写字段
        for field in fields:
            # field: 是CharField/InterField层,或者是嵌套的序列化类
            validate_method = getattr(self, 'validate_' + field.field_name, None)
            # 1.field.get_value 获取Field().get_value()
            primitive_value = field.get_value(data)
            try:
                # 2.调用Field字段的run_validation
                validated_value = field.run_validation(primitive_value)
                if validate_method is not None:
                    validated_value = validate_method(validated_value)
            except ValidationError as exc:
                errors[field.field_name] = exc.detail
            except DjangoValidationError as exc:
                errors[field.field_name] = get_error_detail(exc)
            except SkipField:
                pass
            else:
                set_value(ret, field.source_attrs, validated_value)

        if errors:
            raise ValidationError(errors)

        return ret

    def to_representation(self, instance):
        """
        Object instance -> Dict of primitive datatypes.
        """
        ret = OrderedDict()
        fields = self._readable_fields

        for field in fields:
            try:
                attribute = field.get_attribute(instance)
            except SkipField:
                continue
            check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
            if check_for_none is None:
                ret[field.field_name] = None
            else:
                ret[field.field_name] = field.to_representation(attribute)

        return ret

    def validate(self, attrs):
        """
        到这里时,所有字段已经合法,进行下一步业务逻辑验证.
        """
        return attrs

3.Serializer 和 Field衔接入口

序列化类to_internal_value中编译每一个field字段是,调用Field.run_validation

  • 序列化类.run_validation:
    • Field.validate_empty_values
    • Field.to_internal_value
    • Field.run_validators(value)
class Field(object):

    def get_value(self, dictionary):
        if html.is_html_input(dictionary):
            # HTML forms will represent empty fields as '', and cannot
            # represent None or False values directly.
            if self.field_name not in dictionary:
                if getattr(self.root, 'partial', False):
                    return empty
                return self.default_empty_html
            ret = dictionary[self.field_name]
            if ret == '' and self.allow_null:
                # If the field is blank, and null is a valid value then
                # determine if we should use null instead.
                return '' if getattr(self, 'allow_blank', False) else None
            elif ret == '' and not self.required:
                # If the field is blank, and emptiness is valid then
                # determine if we should use emptiness instead.
                return '' if getattr(self, 'allow_blank', False) else empty
            return ret
        return dictionary.get(self.field_name, empty)
    
    def run_validation(self, data=empty):
        # 1.验证空值
        (is_empty_value, data) = self.validate_empty_values(data)
        if is_empty_value:
            return data
        # 2.调用Field的to_internal_value
        value = self.to_internal_value(data)
        # 3.运行Field的验证器
        self.run_validators(value)
        return value

    def run_validators(self, value):
        errors = []
        for validator in self.validators:
            # 遍历Field的所有验证器.
            if hasattr(validator, 'set_context'):
                validator.set_context(self)
            try:
                # 调用验证器类,对输入值进行验证
                validator(value)
            except ValidationError as exc:
                if isinstance(exc.detail, dict):
                    raise
                errors.extend(exc.detail)
            except DjangoValidationError as exc:
                errors.extend(get_error_detail(exc))
        if errors:
            raise ValidationError(errors)

todo 下次在重新整理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章