djangorestframework源码分析2:serializer序列化数据的执行流程

djangorestframework源码分析
本文环境python3.5.2,djangorestframework (3.5.1)系列
djangorestframework源码分析-serializer的执行流程

本次主要就是分析一下,djangorestframework中的序列化的工作的执行流程,主要是如果进行数据的序列化和返回的。本次示例代码就只展示了后端编写的代码和序列化过程,示例代码如下:

...
from uuid import uuid4
from django.db import models


class TestModel(models.Model):
    uuid = models.UUIDField(primary_key=True, default=uuid4)
    name = models.CharField(verbose_name="产品素材名称", max_length=200, null=True, blank=True)
...


...
from app_name.models import TestModel


class TestSerializer(serializers.ModelSerializer):
    is_authorize = serializers.SerializerMethodField()
    enterprise_name = serializers.SerializerMethodField()
    enterprise_address = serializers.SerializerMethodField()
    enterprise_name = serializers.CharField(
        source='name')

    class Meta:
        model = TestModel
        fields = ("uuid", "is_authorize", "enterprise_name", "enterprise_address", "name")

    def get_enterprise_name(self, obj):
        enterprise_name = obj.name
        return enterprise_name

    def get_is_authorize(self, obj):
        return False

    def get_enterprise_address(self, obj):
        return "--"
...



...
from rest_framework.views import APIView
from app_name.models import TestModel
from rest_framework.response import Response


class TestAPI(APIView):
    """ 测试api"""
    view_name = 'unregister_company'
    permission_classes = (IsAuthenticated, OperatorPermissions)

    def get(self, requset, *args, **kwargs):
        instance = TestModel.objects.first()
        serializer = TestSerializer(instance)
        return Response(serializer.data)
...

其中代码包括了,TestModel、TestSerializer和TestAPI此时,就完成了对应的所有的后端代码,前端路由可自行配置。

Serializer的序列化过程

由于本例中使用了相对比较常见的serializers.ModelSerializer直接对django中的model实例进行序列化,所以先查看该类的定义:

class ModelSerializer(Serializer):                                      # 继承自Serializer
    """
    A `ModelSerializer` is just a regular `Serializer`, except that:

    * A set of default fields are automatically populated.
    * A set of default validators are automatically populated.
    * Default `.create()` and `.update()` implementations are provided.

    The process of automatically determining a set of serializer fields
    based on the model fields is reasonably complex, but you almost certainly
    don't need to dig into the implementation.

    If the `ModelSerializer` class *doesn't* generate the set of fields that
    you need you should either declare the extra/differing fields explicitly on
    the serializer class, or simply use a `Serializer` class.
    """
    serializer_field_mapping = {
        models.AutoField: IntegerField,
        models.BigIntegerField: IntegerField,
        models.BooleanField: BooleanField,
        models.CharField: CharField,
        models.CommaSeparatedIntegerField: CharField,
        models.DateField: DateField,
        models.DateTimeField: DateTimeField,
        models.DecimalField: DecimalField,
        models.EmailField: EmailField,
        models.Field: ModelField,
        models.FileField: FileField,
        models.FloatField: FloatField,
        models.ImageField: ImageField,
        models.IntegerField: IntegerField,
        models.NullBooleanField: NullBooleanField,
        models.PositiveIntegerField: IntegerField,
        models.PositiveSmallIntegerField: IntegerField,
        models.SlugField: SlugField,
        models.SmallIntegerField: IntegerField,
        models.TextField: CharField,
        models.TimeField: TimeField,
        models.URLField: URLField,
        models.GenericIPAddressField: IPAddressField,
        models.FilePathField: FilePathField,
    }                                                                           # django中的字段与restframework中字段的对应关系
    if ModelDurationField is not None:
        serializer_field_mapping[ModelDurationField] = DurationField
    if ModelJSONField is not None:
        serializer_field_mapping[ModelJSONField] = JSONField
    serializer_related_field = PrimaryKeyRelatedField                           # 序列化时候的关联信息
    serializer_related_to_field = SlugRelatedField
    serializer_url_field = HyperlinkedIdentityField
    serializer_choice_field = ChoiceField

    # The field name for hyperlinked identity fields. Defaults to 'url'.
    # You can modify this using the API setting.
    #
    # Note that if you instead need modify this on a per-serializer basis,
    # you'll also need to ensure you update the `create` method on any generic
    # views, to correctly handle the 'Location' response header for
    # "HTTP 201 Created" responses.
    url_field_name = None

从定义可知,ModelSerializer继承自Serializer,并且将django中的字段对应到了restframework中定义的字段,并且还定义了许多对于数据库中外键等关联关系的处理函数,由于此处没有讲述关于外键等场景故不详细分析有关数据库关系时的序列化过程,然后继续查看Serializer的定义:

@six.add_metaclass(SerializerMetaclass)
class Serializer(BaseSerializer):
    default_error_messages = {
        'invalid': _('Invalid data. Expected a dictionary, but got {datatype}.')
    }

    @property
    def fields(self):
        """
        A dictionary of {field_name: field_instance}.
        """
        # `fields` is evaluated lazily. We do this to ensure that we don't
        # have issues importing modules that use ModelSerializers as fields,
        # even if Django's app-loading stage has not yet run.
        if not hasattr(self, '_fields'):
            self._fields = BindingDict(self) 					# 调用filed的bind方法 完成方法绑定
            for key, value in self.get_fields().items():
                self._fields[key] = value
        return self._fields
    ...


class BaseSerializer(Field):
    """
    The BaseSerializer class provides a minimal class which may be used
    for writing custom serializer implementations.

    Note that we strongly restrict the ordering of operations/properties
    that may be used on the serializer in order to enforce correct usage.

    In particular, if a `data=` argument is passed then:

    .is_valid() - Available.
    .initial_data - Available.
    .validated_data - Only available after calling `is_valid()`
    .errors - Only available after calling `is_valid()`
    .data - Only available after calling `is_valid()`

    If a `data=` argument is not passed then:

    .is_valid() - Not available.
    .initial_data - Not available.
    .validated_data - Not available.
    .errors - Not available.
    .data - Available.
    """

    def __init__(self, instance=None, data=empty, **kwargs):
        self.instance = instance
        if data is not empty:
            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)

    ...


class Field(object):
    _creation_counter = 0

    default_error_messages = {
        'required': _('This field is required.'),
        'null': _('This field may not be null.')
    }
    default_validators = []
    default_empty_html = empty
    initial = None

    def __init__(self, read_only=False, write_only=False,
                 required=None, default=empty, initial=empty, source=None,
                 label=None, help_text=None, style=None,
                 error_messages=None, validators=None, allow_null=False):
        self._creation_counter = Field._creation_counter
        Field._creation_counter += 1

        # If `required` is unset, then use `True` unless a default is provided.
        if required is None:
            required = default is empty and not read_only

        # Some combinations of keyword arguments do not make sense.
        assert not (read_only and write_only), NOT_READ_ONLY_WRITE_ONLY
        assert not (read_only and required), NOT_READ_ONLY_REQUIRED
        assert not (required and default is not empty), NOT_REQUIRED_DEFAULT
        assert not (read_only and self.__class__ == Field), USE_READONLYFIELD

        self.read_only = read_only
        self.write_only = write_only
        self.required = required
        self.default = default
        self.source = source
        self.initial = self.initial if (initial is empty) else initial
        self.label = label
        self.help_text = help_text
        self.style = {} if style is None else style
        self.allow_null = allow_null

        if self.default_empty_html is not empty:
            if default is not empty:
                self.default_empty_html = default

        if validators is not None:
            self.validators = validators[:]

        # These are set up by `.bind()` when the field is added to a serializer.
        self.field_name = None
        self.parent = None

        # Collect default error message from self and parent classes
        messages = {}
        for cls in reversed(self.__class__.__mro__):
            messages.update(getattr(cls, 'default_error_messages', {}))
        messages.update(error_messages or {})
        self.error_messages = messages

    ...

通过简单的定义可知,Serializer继承自BaseSerializer,BaseSerializer继承自Field,在Serializer在定义的时候还使用了SerializerMetaclass元类定义来操控属性;

class SerializerMetaclass(type):
    """
    This metaclass sets a dictionary named `_declared_fields` on the class.

    Any instances of `Field` included as attributes on either the class
    or on any of its superclasses will be include in the
    `_declared_fields` dictionary.
    """

    @classmethod
    def _get_declared_fields(cls, bases, attrs):
        fields = [(field_name, attrs.pop(field_name))
                  for field_name, obj in list(attrs.items())
                  if isinstance(obj, Field)]                    # 检查属性是否是Field,如果是则从attrs中删除弹出
        fields.sort(key=lambda x: x[1]._creation_counter)       # 重新排序

        # If this class is subclassing another Serializer, add that Serializer's
        # fields.  Note that we loop over the bases in *reverse*. This is necessary
        # in order to maintain the correct order of fields.
        for base in reversed(bases):                            # 排序基类
            if hasattr(base, '_declared_fields'):
                fields = list(base._declared_fields.items()) + fields       # 将所有父类的字段都相加

        return OrderedDict(fields)

    def __new__(cls, name, bases, attrs):
        attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)              # 在创建类的时候添加属性_declared_fields
        return super(SerializerMetaclass, cls).__new__(cls, name, bases, attrs)         # 生成类

梳理了大概的继承关系和其中的一些元类知识后,查看代码

serializer = TestSerializer(data=instance)

继续分析TestSerializer的初始化过程,

BaseSerializer类的初始化方法
def __init__(self, instance=None, data=empty, **kwargs):
    self.instance = instance 												# 设置实例
    if data is not empty:
        self.initial_data = data 					 						# 设置序列化传入的数据
    self.partial = kwargs.pop('partial', False) 							# 是否是更新
    self._context = kwargs.pop('context', {}) 								# 获取上下文
    kwargs.pop('many', None) 												# 删除 many参数
    super(BaseSerializer, self).__init__(**kwargs) 							# 调用Field的初始化方法

Field的初始化方法
def __init__(self, read_only=False, write_only=False,
             required=None, default=empty, initial=empty, source=None,
             label=None, help_text=None, style=None,
             error_messages=None, validators=None, allow_null=False):
    self._creation_counter = Field._creation_counter
    Field._creation_counter += 1

    # If `required` is unset, then use `True` unless a default is provided.
    if required is None:
        required = default is empty and not read_only

    # Some combinations of keyword arguments do not make sense. 			# 进行相关逻辑的检查
    assert not (read_only and write_only), NOT_READ_ONLY_WRITE_ONLY
    assert not (read_only and required), NOT_READ_ONLY_REQUIRED
    assert not (required and default is not empty), NOT_REQUIRED_DEFAULT
    assert not (read_only and self.__class__ == Field), USE_READONLYFIELD

    self.read_only = read_only
    self.write_only = write_only
    self.required = required
    self.default = default
    self.source = source
    self.initial = self.initial if (initial is empty) else initial
    self.label = label
    self.help_text = help_text
    self.style = {} if style is None else style
    self.allow_null = allow_null

    if self.default_empty_html is not empty:
        if default is not empty:
            self.default_empty_html = default

    if validators is not None:
        self.validators = validators[:]

    # These are set up by `.bind()` when the field is added to a serializer.
    self.field_name = None
    self.parent = None

    # Collect default error message from self and parent classes
    messages = {}
    for cls in reversed(self.__class__.__mro__):
        messages.update(getattr(cls, 'default_error_messages', {}))
    messages.update(error_messages or {})
    self.error_messages = messages

当初始化完成后就执行了如下代码;

Response(serializer.data)

此时查看serializer.data的属性方法,由于ModelSerializer没有重写data属性所以就调用了Serializer的data属性;

Serializer的data属性
@property
def data(self):
    ret = super(Serializer, self).data              # 调用了父类BaseSerialzier的data属性
    return ReturnDict(ret, serializer=self)

BaseSerializer的data属性
@property
def data(self):
    if hasattr(self, 'initial_data') and not hasattr(self, '_validated_data'):
        msg = (
            'When a serializer is passed a `data` keyword argument you '
            'must call `.is_valid()` before attempting to access the '
            'serialized `.data` representation.\n'
            'You should either call `.is_valid()` first, '
            'or access `.initial_data` instead.'
        )
        raise AssertionError(msg)                                           # 如果要访问data属性必须先调用is_valid方法进行检查

    if not hasattr(self, '_data'):                                          # 如果没有_data属性
        if self.instance is not None and not getattr(self, '_errors', None):    # 实例不为空并且没有_errors属性
            self._data = self.to_representation(self.instance)              # 调用self.to_representation获取结果
        elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None):   # 如果有is_valid后的数据并没有检查出错误则调用to_representation处理
            self._data = self.to_representation(self.validated_data)        # 
        else:
            self._data = self.get_initial()                                 # 如果都不符合则调用get_initial处理
    return self._data

由此本文的例子是传入的instance,在执行self.to_representation函数时,就传入了instance实例,

Serializer的to_representation方法
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)       # 获取实例中对应的field字段
        except SkipField:
            continue

        # We skip `to_representation` for `None` values so that fields do
        # not have to explicitly deal with that case.
        #
        # For related fields with `use_pk_only_optimization` we need to
        # resolve the pk value.
        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)      # 否则调用field的to_representation来处理attribute

    return ret      # 返回结果

@cached_property
def _readable_fields(self):
    return [
        field for field in self.fields.values()
        if not field.write_only
    ]                                                       # 在初始化的时候就获取只读的字段值
    
@property
def fields(self):
    """
    A dictionary of {field_name: field_instance}.
    """
    # `fields` is evaluated lazily. We do this to ensure that we don't
    # have issues importing modules that use ModelSerializers as fields,
    # even if Django's app-loading stage has not yet run.
    if not hasattr(self, '_fields'):                        # 检查是否有_fields属性
        self._fields = BindingDict(self)                        
        for key, value in self.get_fields().items():        # 调用get_fields获取字段属性值
            self._fields[key] = value                       # 写入该值
    return self._fields                                     # 返回该字段

由于由于此时是ModelSerializer此时调用了该类的get_fields方法;

def get_fields(self):
    """
    Return the dict of field names -> field instances that should be
    used for `self.fields` when instantiating the serializer.
    """
    if self.url_field_name is None:                                     # 检查url_field_name
        self.url_field_name = api_settings.URL_FIELD_NAME

    assert hasattr(self, 'Meta'), (
        'Class {serializer_class} missing "Meta" attribute'.format(
            serializer_class=self.__class__.__name__
        )
    )                                                                   # 必须配置Meta属性
    assert hasattr(self.Meta, 'model'), (
        'Class {serializer_class} missing "Meta.model" attribute'.format(
            serializer_class=self.__class__.__name__
        )
    )                                                                   # 必须在Meta中配置model属性
    if model_meta.is_abstract_model(self.Meta.model):                   # 如果是抽象则直接报错
        raise ValueError(
            'Cannot use ModelSerializer with Abstract Models.'
        )           

    declared_fields = copy.deepcopy(self._declared_fields)              # 深拷贝所有字段
    model = getattr(self.Meta, 'model')                                 # 获取model
    depth = getattr(self.Meta, 'depth', 0)                              # 获取深度信息

    if depth is not None:                                               # 深度必须大于等于0小于等于10
        assert depth >= 0, "'depth' may not be negative."
        assert depth <= 10, "'depth' may not be greater than 10."

    # Retrieve metadata about fields & relationships on the model class.
    info = model_meta.get_field_info(model)                             # 获取model的信息
    field_names = self.get_field_names(declared_fields, info)           # 获取filed字段名称

    # Determine any extra field arguments and hidden fields that
    # should be included
    extra_kwargs = self.get_extra_kwargs()                              # 获取额外参数
    extra_kwargs, hidden_fields = self.get_uniqueness_extra_kwargs(
        field_names, declared_fields, extra_kwargs
    )

    # Determine the fields that should be included on the serializer.
    fields = OrderedDict()

    for field_name in field_names:                                      # 遍历字段名称
        # If the field is explicitly declared on the class then use that.
        if field_name in declared_fields:                               # 如果在初始化的字段中
            fields[field_name] = declared_fields[field_name]            # 直接设置比进行下一个
            continue

        # Determine the serializer field class and keyword arguments.
        field_class, field_kwargs = self.build_field(
            field_name, info, model, depth
        )                                                               

        # Include any kwargs defined in `Meta.extra_kwargs`
        extra_field_kwargs = extra_kwargs.get(field_name, {})
        field_kwargs = self.include_extra_kwargs(
            field_kwargs, extra_field_kwargs
        )                                                               # 获取额外定义的字段

        # Create the serializer field.
        fields[field_name] = field_class(**field_kwargs)                # 创建额外字段的field实例

    # Add in any hidden fields.
    fields.update(hidden_fields)

    return fields                                                       # 返回实例

此时就通过Model转换成了序列化中渲染的字段值,在获取属性的过程中,
其中filed.get_attribute方法,其实就是调用了如下方法;

def get_attribute(instance, attrs):
    """
    Similar to Python's built in `getattr(instance, attr)`,
    but takes a list of nested attributes, instead of a single attribute.

    Also accepts either attribute lookup on objects or dictionary lookups.
    """
    for attr in attrs:                                                              # 遍历属性列表
        if instance is None:                                                        # 如果实例为空
            # Break out early if we get `None` at any point in a nested lookup.
            return None
        try:
            if isinstance(instance, collections.Mapping):                           # 检查是否为Mapping
                instance = instance[attr]                                           # 直接获取属性
            else:
                instance = getattr(instance, attr)                                  # 直接获取实例的该属性
        except ObjectDoesNotExist:
            return None
        if is_simple_callable(instance):
            try:
                instance = instance()
            except (AttributeError, KeyError) as exc:
                # If we raised an Attribute or KeyError here it'd get treated
                # as an omitted field in `Field.get_attribute()`. Instead we
                # raise a ValueError to ensure the exception is not masked.
                raise ValueError('Exception raised in callable attribute "{0}"; original exception was: {1}'.format(attr, exc))

    return instance                                                                 # 返回

此时,在本例的序列化定义如下;

    is_authorize = serializers.SerializerMethodField()
    enterprise_name = serializers.SerializerMethodField()
    enterprise_address = serializers.SerializerMethodField()
    enterprise_name = serializers.CharField(
        source='name')

此时需要渲染的字段有(“uuid”, “is_authorize”, “enterprise_name”, “enterprise_address”, “name”),

此时uuid和name两个字段值由于没有显示定义,则通过生成一个对应属性的filed实例来处理,此时就调用了filed的to_representation方法,其中serializers.SerializerMethodField定义如下;

class SerializerMethodField(Field):
    """
    A read-only field that get its representation from calling a method on the
    parent serializer class. The method called will be of the form
    "get_{field_name}", and should take a single argument, which is the
    object being serialized.

    For example:

    class ExampleSerializer(self):
        extra_info = SerializerMethodField()

        def get_extra_info(self, obj):
            return ...  # Calculate some data to return.
    """
    def __init__(self, method_name=None, **kwargs):
        self.method_name = method_name                                      # 初始化 method_name默认为空
        kwargs['source'] = '*'                                          
        kwargs['read_only'] = True                                          # 设置read_only为True
        super(SerializerMethodField, self).__init__(**kwargs)               # 调用父类的初始化方法

    def bind(self, field_name, parent):
        # In order to enforce a consistent style, we error if a redundant
        # 'method_name' argument has been used. For example:
        # my_field = serializer.SerializerMethodField(method_name='get_my_field')
        default_method_name = 'get_{field_name}'.format(field_name=field_name)      # 获取默认的方法名称
        assert self.method_name != default_method_name, (
            "It is redundant to specify `%s` on SerializerMethodField '%s' in "
            "serializer '%s', because it is the same as the default method name. "
            "Remove the `method_name` argument." %
            (self.method_name, field_name, parent.__class__.__name__)
        )                                                                           # 如果定义的名称和名称相符则报错

        # The method name should default to `get_{field_name}`.
        if self.method_name is None:
            self.method_name = default_method_name                                  # 判断是否使用默认的方法名称

        super(SerializerMethodField, self).bind(field_name, parent)                 # 调用父类的bind方法

    def to_representation(self, value):
        method = getattr(self.parent, self.method_name)                     #  获取该方法
        return method(value)                                                # 并将该值传入并执行

此时就调用了定义的方法;

    def get_enterprise_name(self, obj):
        enterprise_name = obj.name
        return enterprise_name

等自定义的方法,此时就获取了所有的序列化数据,至此一个简单的Serializer的过程就基本执行完成。

总结

本文只是简单的分了一下ModelSerializer的序列化过程,Serializer的序列化还包括了数据的创建更新等更丰富的操作,再次就没有一一列举,大家有兴趣课自行分析,由于水平有限,如有疏漏请批评指正。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值