djangorestframework源码分析2:serializer序列化数据的执行流程
djangorestframework源码分析
本文环境python3.5.2,djangorestframework (3.5.1)系列
djangorestframework源码分析-serializer的执行流程
本次主要就是分析一下,djangorestframework中的序列化的工作的执行流程,主要是如果进行数据的序列化和返回的。本次示例代码就只展示了后端编写的代码和序列化过程,示例代码如下:
...
from uuid import uuid4
from django.db import modelsclass 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 TestModelclass TestSerializer(serializers.ModelSerializer):is_authorize = serializers.SerializerMethodField()enterprise_name = serializers.SerializerMethodField()enterprise_address = serializers.SerializerMethodField()enterprise_name = serializers.CharField(source='name')class Meta:model = TestModelfields = ("uuid", "is_authorize", "enterprise_name", "enterprise_address", "name")def get_enterprise_name(self, obj):enterprise_name = obj.namereturn enterprise_namedef get_is_authorize(self, obj):return Falsedef get_enterprise_address(self, obj):return "--"
......
from rest_framework.views import APIView
from app_name.models import TestModel
from rest_framework.response import Responseclass 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 fieldsbased on the model fields is reasonably complex, but you almost certainlydon't need to dig into the implementation.If the `ModelSerializer` class *doesn't* generate the set of fields thatyou need you should either declare the extra/differing fields explicitly onthe 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] = DurationFieldif ModelJSONField is not None:serializer_field_mapping[ModelJSONField] = JSONFieldserializer_related_field = PrimaryKeyRelatedField # 序列化时候的关联信息serializer_related_to_field = SlugRelatedFieldserializer_url_field = HyperlinkedIdentityFieldserializer_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}.')}@propertydef 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] = valuereturn self._fields...class BaseSerializer(Field):"""The BaseSerializer class provides a minimal class which may be usedfor writing custom serializer implementations.Note that we strongly restrict the ordering of operations/propertiesthat 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 = instanceif data is not empty:self.initial_data = dataself.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 = 0default_error_messages = {'required': _('This field is required.'),'null': _('This field may not be null.')}default_validators = []default_empty_html = emptyinitial = Nonedef __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_counterField._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_ONLYassert not (read_only and required), NOT_READ_ONLY_REQUIREDassert not (required and default is not empty), NOT_REQUIRED_DEFAULTassert not (read_only and self.__class__ == Field), USE_READONLYFIELDself.read_only = read_onlyself.write_only = write_onlyself.required = requiredself.default = defaultself.source = sourceself.initial = self.initial if (initial is empty) else initialself.label = labelself.help_text = help_textself.style = {} if style is None else styleself.allow_null = allow_nullif self.default_empty_html is not empty:if default is not empty:self.default_empty_html = defaultif validators is not None:self.validators = validators[:]# These are set up by `.bind()` when the field is added to a serializer.self.field_name = Noneself.parent = None# Collect default error message from self and parent classesmessages = {}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 classor on any of its superclasses will be include in the`_declared_fields` dictionary."""@classmethoddef _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_fieldsreturn 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_counterField._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_ONLYassert not (read_only and required), NOT_READ_ONLY_REQUIREDassert not (required and default is not empty), NOT_REQUIRED_DEFAULTassert not (read_only and self.__class__ == Field), USE_READONLYFIELDself.read_only = read_onlyself.write_only = write_onlyself.required = requiredself.default = defaultself.source = sourceself.initial = self.initial if (initial is empty) else initialself.label = labelself.help_text = help_textself.style = {} if style is None else styleself.allow_null = allow_nullif self.default_empty_html is not empty:if default is not empty:self.default_empty_html = defaultif validators is not None:self.validators = validators[:]# These are set up by `.bind()` when the field is added to a serializer.self.field_name = Noneself.parent = None# Collect default error message from self and parent classesmessages = {}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 attributeif check_for_none is None: # 如果为空ret[field.field_name] = None # 则为空else:ret[field.field_name] = field.to_representation(attribute) # 否则调用field的to_representation来处理attributereturn 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 beused for `self.fields` when instantiating the serializer."""if self.url_field_name is None: # 检查url_field_nameself.url_field_name = api_settings.URL_FIELD_NAMEassert 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') # 获取modeldepth = getattr(self.Meta, 'depth', 0) # 获取深度信息if depth is not None: # 深度必须大于等于0小于等于10assert 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 includedextra_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 Nonetry:if isinstance(instance, collections.Mapping): # 检查是否为Mappinginstance = instance[attr] # 直接获取属性else:instance = getattr(instance, attr) # 直接获取实例的该属性except ObjectDoesNotExist:return Noneif 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 theparent serializer class. The method called will be of the form"get_{field_name}", and should take a single argument, which is theobject 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为Truesuper(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.namereturn enterprise_name
等自定义的方法,此时就获取了所有的序列化数据,至此一个简单的Serializer的过程就基本执行完成。
总结
本文只是简单的分了一下ModelSerializer的序列化过程,Serializer的序列化还包括了数据的创建更新等更丰富的操作,再次就没有一一列举,大家有兴趣课自行分析,由于水平有限,如有疏漏请批评指正。
djangorestframework源码分析2:serializer序列化数据的执行流程相关推荐
- mysql8.0源代码解析_MySQL8.0.11源码分析之mysql关键函数和执行流程
mysql是命令行客户端程序 ,交互式输入SQL语句或从文件以批处理模式执行它们的命令行工具. 入口函数 int main(int argc, char *argv[]) { if (get_opti ...
- djangorestframework源码分析1:generics中的view执行流程
djangorestframework源码分析 本文环境python3.5.2,djangorestframework (3.5.1)系列 djangorestframework源码分析-generi ...
- Spark 源码分析之ShuffleMapTask内存数据Spill和合并
Spark 源码分析之ShuffleMapTask内存数据Spill和合并 更多资源分享 SPARK 源码分析技术分享(视频汇总套装视频): https://www.bilibili.com/vide ...
- springboot集成mybatis源码分析-mybatis的mapper执行查询时的流程(三)
springboot集成mybatis源码分析-mybatis的mapper执行查询时的流程(三) 例: package com.example.demo.service;import com.exa ...
- React Native 源码分析(三)——Native View创建流程
1.React Native 源码分析(一)-- 启动流程 2.React Native 源码分析(二)-- 通信机制 3.React Native 源码分析(三)-- Native View创建流程 ...
- Linux brk(),mmap()系统调用源码分析3:brk()的内存申请流程
Linux brk(),mmap()系统调用源码分析 brk()的内存申请流程 荣涛 2021年4月30日 内核版本:linux-5.10.13 注释版代码:https://github.com/Rt ...
- SRS流媒体服务器源码分析(一):Rtmp publish流程
1.线程模型 srs使用了state-threads协程库,是单线程多协程模型. 这个协程的概念类似于lua的协程,都是单线程中可以创建多个协程.而golang中的goroutine协程是多线程并发的 ...
- Mybatis 源码分析(一)配置文件加载流程
Mybatis 源码分析(一)配置文件加载流程 1.项目构建 引入依赖 <dependency><groupId>org.mybatis</groupId>< ...
- 【Android SDM660源码分析】- 02 - UEFI XBL QcomChargerApp充电流程代码分析
[Android SDM660源码分析]- 02 - UEFI XBL QcomChargerApp充电流程代码分析 一.加载 UEFI 默认应用程序 1.1 LaunchDefaultBDSApps ...
最新文章
- 希捷期望HAMR实现其营收的增长
- hibernate实体的几种状态:
- 数据库系备份相关知识笔记
- python获得距离指定(x,y)固定距离的全部坐标点
- python的实例属性_python 实例属性和类属性
- 为什么不可变性在 React 中非常重要
- leetcode/力扣 二叉树题目大总结,BAT程序员完整学习手册PDF开放下载!
- arcgis导出地图分辨率多少合适_地图册制作:ArcGIS分幅制图——以深圳地铁线路图为例...
- 表格排版及其表格嵌套
- 在window中nginx安装腾讯云ssl报错nginx: [emerg] cannot load certificate
- 牛逼的python代码_几段牛逼的 Python 代码理解面向对象
- Windows去掉文件名的括号
- 【Numba】加速计算
- 新浪微博定位页面代码解析
- sql语言学习——数据定义语言
- python中jieba库的安装
- 计算机网络--自顶向下方法学习笔记
- 如何手动创建 k8s kubeconfig 文件以实现多环境切换
- 怎么设置ep4ce6e22b8n引脚_利用EP4CE6E22做一块FPGA核心转接板(原理图、PCB源文件、测试程序)...
- Layui表格重载 模糊查询
热门文章
- 打造一流创新环境:协作、开放、可持续
- 微软收购 GitHub 两年后,大咖共论开源新生态
- 专访百度熊辉:有人转AI纯粹因为好找工作,这样的人不是我想要的
- 惊艳!基于RNN的AI写词机竟能写出如此优秀的情诗!
- 重磅 | TensorFlow 2.0即将发布,所有tf.contrib将被弃用
- 企业智能化升级之路:CSDN《2017-2018中国人工智能产业路线图V1.0》重磅发布
- 都是套路!要毁灭人类、喷马斯克、还获得公民身份的机器人Sophia,真相确是...
- Kafka 顺序消费方案
- Kafka 原理以及分区分配策略剖析
- 早就听闻阿里开源的 Arthas 在做 Java 应用诊断上十分牛逼,没失望