首页 > 其他 > 详细

django rest framework 反序列化过程剖析

时间:2021-04-08 18:27:44      阅读:19      评论:0      收藏:0      [点我收藏+]

前天写了序列化过程,今天就水一篇反序列化过程吧。

反序列化一般都在create,update方法中被使用,如:

class CreateModelMixin:
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

    def get_success_headers(self, data):
        try:
            return {Location: str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {}

根据前天所述的继承关系这里serializer.is_valid()调用的就是rest_framework.serilizers.BaseSerializer的is_valid方法,用该方法去反序列化去检查输入的数据是不是符合serilizer类中定义的数据格式

class BaseSerializer(Field):
    def is_valid(self, raise_exception=False):
        assert not hasattr(self, restore_object), (
            Serializer `%s.%s` has old-style version 2 `.restore_object()` 
            that is no longer compatible with REST framework 3. 
            Use the new-style `.create()` and `.update()` methods instead. %
            (self.__class__.__module__, self.__class__.__name__)
        )

        assert hasattr(self, initial_data), (
            Cannot call `.is_valid()` as no `data=` keyword argument was 
            passed when instantiating the serializer instance.
        )

        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)

run_validation方法在rest_framework.serilizers.Serializer类中被定义

class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
    def run_validation(self, data=empty):
        """
        We override the default `run_validation`, because the validation
        performed by validators and the `.validate()` method should
        be coerced into an error dictionary with a ‘non_fields_error‘ key.
        """
        (is_empty_value, data) = self.validate_empty_values(data)
        if is_empty_value:
            return data

        value = self.to_internal_value(data)
        try:
            self.run_validators(value)
            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 run_validators(self, value):
        """
        Add read_only fields with defaults to value before running validators.
        """
        if isinstance(value, dict):
            to_validate = self._read_only_defaults()
            to_validate.update(value)
        else:
            to_validate = value
        super().run_validators(to_validate)

    def to_internal_value(self, data):
        """
        Dict of native values <- Dict of primitive datatypes.
        """
        if not isinstance(data, Mapping):
            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

        for field in fields:
            validate_method = getattr(self, validate_ + field.field_name, None)
            primitive_value = field.get_value(data)
            try:
                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

在to_internal_value中对字段做反序列化校验,然后在run_validators中对serilizers类中定义的validators规则做校验

校验通过后使用serializer.save保存数据,serializer.save()调用的就是rest_framework.serilizers.BaseSerializer的save方法

class BaseSerializer(Field):
    def save(self, **kwargs):
        assert not hasattr(self, save_object), (
            Serializer `%s.%s` has old-style version 2 `.save_object()` 
            that is no longer compatible with REST framework 3. 
            Use the new-style `.create()` and `.update()` methods instead. %
            (self.__class__.__module__, self.__class__.__name__)
        )

        assert hasattr(self, _errors), (
            You must call `.is_valid()` before calling `.save()`.
        )

        assert not self.errors, (
            You cannot call `.save()` on a serializer with invalid data.
        )

        # Guard against incorrect use of `serializer.save(commit=False)`
        assert commit not in kwargs, (
            "‘commit‘ is not a valid keyword argument to the ‘save()‘ method. "
            "If you need to access data before committing to the database then "
            "inspect ‘serializer.validated_data‘ instead. "
            "You can also pass additional keyword arguments to ‘save()‘ if you "
            "need to set extra attributes on the saved model instance. "
            "For example: ‘serializer.save(owner=request.user)‘.‘"
        )

        assert not hasattr(self, _data), (
            "You cannot call `.save()` after accessing `serializer.data`."
            "If you need to access data before committing to the database then "
            "inspect ‘serializer.validated_data‘ instead. "
        )

        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

在这里用self.instance判断是不是创建还是更新(即是create还是update)所以也会调用相应的方法,就拿create来说

在这里调用的就是rest_framework.serilizers.ModelSerializer类的create方法(update时也是同理)

class ModelSerializer(Serializer):
    def create(self, validated_data):
        """
        We have a bit of extra checking around this in order to provide
        descriptive messages when something goes wrong, but this method is
        essentially just:

            return ExampleModel.objects.create(**validated_data)

        If there are many to many fields present on the instance then they
        cannot be set until the model is instantiated, in which case the
        implementation is like so:

            example_relationship = validated_data.pop(‘example_relationship‘)
            instance = ExampleModel.objects.create(**validated_data)
            instance.example_relationship = example_relationship
            return instance

        The default implementation also does not handle nested relationships.
        If you want to support writable nested relationships you‘ll need
        to write an explicit `.create()` method.
        """
        raise_errors_on_nested_writes(create, self, validated_data)

        ModelClass = self.Meta.model

        # Remove many-to-many relationships from validated_data.
        # They are not valid arguments to the default `.create()` method,
        # as they require that the instance has already been saved.
        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_data):
                many_to_many[field_name] = validated_data.pop(field_name)

        try:
            instance = ModelClass._default_manager.create(**validated_data)
        except TypeError:
            tb = traceback.format_exc()
            msg = (
                Got a `TypeError` when calling `%s.%s.create()`. 
                This may be because you have a writable field on the 
                serializer class that is not a valid argument to 
                `%s.%s.create()`. You may need to make the field 
                read-only, or override the %s.create() method to handle 
                this correctly.\nOriginal exception was:\n %s %
                (
                    ModelClass.__name__,
                    ModelClass._default_manager.name,
                    ModelClass.__name__,
                    ModelClass._default_manager.name,
                    self.__class__.__name__,
                    tb
                )
            )
            raise TypeError(msg)

        # Save many-to-many relationships after the instance is created.
        if many_to_many:
            for field_name, value in many_to_many.items():
                field = getattr(instance, field_name)
                field.set(value)

        return instance

    def update(self, instance, validated_data):
        raise_errors_on_nested_writes(update, self, validated_data)
        info = model_meta.get_field_info(instance)

        # Simply set each attribute on the instance, and then save it.
        # Note that unlike `.create()` we don‘t need to treat many-to-many
        # relationships as being a special case. During updates we already
        # have an instance pk for the relationships to be associated with.
        m2m_fields = []
        for attr, value in validated_data.items():
            if attr in info.relations and info.relations[attr].to_many:
                m2m_fields.append((attr, value))
            else:
                setattr(instance, attr, value)

        instance.save()

        # Note that many-to-many fields are set after updating instance.
        # Setting m2m fields triggers signals which could potentially change
        # updated instance and we do not want it to collide with .update()
        for attr, value in m2m_fields:
            field = getattr(instance, attr)
            field.set(value)

        return instance

注意了这里self.Meta.model是在serilizer类中定义了的数据库模型类

这里调用_default_manager.create去将数据保存到数据库

django rest framework 反序列化过程剖析

原文:https://www.cnblogs.com/arrow-kejin/p/14632441.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!