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 下次在重新整理

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