1. 源码分析

注意:以下代码片段为方便理解已进行简化,只保留了与序列化功能相关的代码

序列化的源码中涉及到了元类的概念,我在这里简单说明一下:元类(metaclass)是一个高级概念,用于定义类的创建行为。简单来说,元类是创建类的类,它决定了类的创建方式和行为。

在 Python 中一切皆为对象,包括类。每个类都有一个元类,它定义了如何创建这个类。通常情况下 Python 会使用默认的元类 type 来创建类。但是,当我们需要对类的创建过程进行自定义时,就可以使用元类,举例:

class Mytype(type)
def __new__(cls,name,bases,attrs): # 类名,继承的父类 ,成员
# 此处可对要创建的类进行操作
del attrs["v1"]
attrs["name"] = "harry" xx = super().__new__(cls,name,bases,attrs) # 调用type类创建对象(这个对象就是Bar类)
retyrn xx class Bar(object, metaclass=Mytype) # metaclass指定自定义元类
v1 = 123 def func(self):
pass 由于元类中删除了v1属性,且增加了name属性,因此此时Bar中无v1属性,且多了name属性

另:父类如果指定了元类metaclass,则其子类默认是用该元类来创建类

补充:实例化Bar类时,相当于是 type对象(),因此会触发type类的__call__方法,其中就调用了Bar的__new__和__init__,因此在实例化类时才会自动触发类的__new__和__init__方法。本质上是因为 对象() 而调用了type元类的call方法;

Serializers组件主要有两个功能:序列化和数据校验

  1. 序列化部分:

    首先定义一个序列化类
class DepartSerializer(serializers.Serializer):
'''Serializer校验'''
# 内置校验
title = serializers.CharField(required=True, max_length=20, min_length=6)
order = serializers.IntegerField(required=False, max_value=100, min_value=10)
count = serializers.ChoiceField(choices=[(1, "高级"), (2, "中级")])

查看Serializer的父类,可知其是通过SerializerMetaclass元类创建的

Serializer(BaseSerializer, metaclass=SerializerMetaclass)

SerializerMetaclass元类源码:

class SerializerMetaclass(type):
@classmethod
def _get_declared_fields(cls, bases, attrs):
fields = [(field_name, attrs.pop(field_name)) # 通过循环获取field字段对象
for field_name, obj in list(attrs.items())
if isinstance(obj, Field)]
fields.sort(key=lambda x: x[1]._creation_counter) known = set(attrs)
def visit(name):
known.add(name)
return name base_fields = [
(visit(name), f)
for base in bases if hasattr(base, '_declared_fields')
for name, f in base._declared_fields.items() if name not in known
] return OrderedDict(base_fields + fields) def __new__(cls, name, bases, attrs):
attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs) # 为类中增加了_declared_fields属性,其中封装了所有的Field字段名及对应的对象
return super().__new__(cls, name, bases, attrs)

通过serializer.data触发序列化流程:

    @property
def data(self):
ret = super().data # 寻找其父类BaseSerializer的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) if not hasattr(self, '_data'):
if self.instance is not None and not getattr(self, '_errors', None):
self._data = self.to_representation(self.instance) # 执行to_representation方法获取序列化数据
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

to_representation方法源码(核心):

    def to_representation(self, instance):
ret = OrderedDict()
fields = self._readable_fields # 筛选出可读的字段对象(其内部对_declared_fields字段进行了深拷贝) for field in fields:
try:
attribute = field.get_attribute(instance) # 循环字段对象列表,并执行get_attribute方法获取对应的值
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) # 执行to_representation转换格式,并将所有数据封装到ret字典中 return ret

get_attribute方法源码:

def get_attribute(self, instance):
return get_attribute(instance, self.source_attrs)
def get_attribute(instance, attrs): # attrs为source字段值  instance为模型对象
for attr in attrs:
try:
if isinstance(instance, Mapping):
instance = instance[attr]
else:
instance = getattr(instance, attr) # 循环获取模型对象最终的attr的值
except ObjectDoesNotExist:
return None
return instance # 返回该字段值

2. 数据校验部分

使用is_valid方法校验数据,获取_errors数据,_errors存在则is_valid返回False。在执行该函数的过程中,触发了run_validation方法:

    def is_valid(self, raise_exception=False):
if not hasattr(self, '_validated_data'):
try: # 触发了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)****

run_validation方法,注意该方法是Serializer类下的方法,不是Field类的方法。在to_internal_value方法中调用字段内置校验,并执行钩子函数。

    def run_validation(self, data=empty):

        (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

to_internal_value方法,fileds从_declared_fields中深拷贝而得到,且只包含了只写的字段对象

    def to_internal_value(self, data):
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

run_validation内置校验:

    def run_validation(self, data=empty):
if data == '' or (self.trim_whitespace and str(data).strip() == ''):
if not self.allow_blank:
self.fail('blank')
return ''
return super().run_validation(data) # 父类的run_validation方法
def run_validation(self, data=empty): (is_empty_value, data) = self.validate_empty_values(data)
if is_empty_value:
return data
value = self.to_internal_value(data)
self.run_validators(value) # 调用字段定义的run_validators进行校验
return value

2、源码改编:

  • 自定义钩子:让某字段既能支持前端传入,又能自定义序列化返回的值;(SerializerMethodField默认是只可读的,用户无法输入,而普通field又无法自定义复杂逻辑返回值)

思路:在调用ser.data开始序列化后的to_representation方法中判断有无自定义格式的钩子,如果有则替换掉该字段对象的值

    def to_representation(self, instance):
ret = OrderedDict()
fields = self._readable_fields for field in fields:
if hasattr(self, 'get_%s' % field.field_name): # 判断是否有"get_xxx"形式的函数,如则执行该方法并将instance传入
value = getattr(self, 'get_%s' % field.field_name)(instance)
ret[field.field_name] = value
else:
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

如果其他类中也需要使用该重写方法,可将该重新方法封装成类,其他类中继承该类后,即可不用每次都重写to_representation方法

DRF-Serializers序列化器组件源码分析及改编的更多相关文章

  1. DRF的限流组件(源码分析)

    DRF限流组件(源码分析) 限流,限制用户访问频率,例如:用户1分钟最多访问100次 或者 短信验证码一天每天可以发送50次, 防止盗刷. 对于匿名用户,使用用户IP作为唯一标识. 对于登录用户,使用 ...

  2. DRF的Serializer组件(源码分析)

    DRF的Serializer组件(源码分析) 1. 数据校验 drf中为我们提供了Serializer,他主要有两大功能: 对请求数据校验(底层调用Django的Form和ModelForm) 对数据 ...

  3. Django框架之drf:8、断点调试,权限、认证、频率组件源码分析,基于APIView编写分页,异常处理

    Django框架之drf 一.断点调式使用 ​ 指,在我们编写代码的时候,程序运行出现报错是无可避免的,当程序 出现报错时,我们需要找到出现报错的代码进行修改,如果时简短的代码很容易就可以找到报错位置 ...

  4. DRF的认证组件(源码分析)

    DRF认证组件(源码分析) 1. 数据库建立用户表 在drf中也给我们提供了 认证组件 ,帮助我们快速实现认证相关的功能,例如: # models.py from django.db import m ...

  5. rest_framework解析器组件源码流程

    rest_framework解析器组件源码流程 解析器顾名思义就是对请求体进行解析.为什么要有解析器?原因很简单,当后台和前端进行交互的时候数据类型不一定都是表单数据或者json,当然也有其他类型的数 ...

  6. 开源MyBatisGenerator组件源码分析

    开源MyBatisGenerator组件源码分析 看源码前,先了解Generator能做什么? MyBatisGenerator是用来生成mybatis的Mapper接口和xml文件的工具,提供多种启 ...

  7. Django-restframework 源码之认证组件源码分析

    Django-restframework 源码之认证组件源码分析 一 前言 之前在 Django-restframework 的流程分析博客中,把最重要的关于认证.权限和频率的方法找到了.该方法是 A ...

  8. element-ui 组件源码分析整理笔记目录

    element-ui button组件 radio组件源码分析整理笔记(一) element-ui switch组件源码分析整理笔记(二) element-ui inputNumber.Card .B ...

  9. DRF-解析器组件源码解析

    解析器组件源码解析 解析器组件源码解析 1 执行request.data 开始找重装的request中的data方法 2 在dispatch找到重装的request def dispatch(self ...

  10. ceph-csi组件源码分析(1)-组件介绍与部署yaml分析

    更多ceph-csi其他源码分析,请查看下面这篇博文:kubernetes ceph-csi分析目录导航 ceph-csi组件源码分析(1)-组件介绍与部署yaml分析 基于tag v3.0.0 ht ...

随机推荐

  1. [学习笔记] 斜率优化DP - DP

    这个真的好容易啊 --wzw 斜率优化dP 例题 [SDOI2012] 任务安排 毒瘤题,让我惨淡经营了两天.这道题luogu有简单版,可以先去看简单版. 显然这是一只DP题,直接开始推狮子.令 dp ...

  2. 利用Stream实现简单的等差数列求和

    我们都熟知高斯的故事,认识等差数列也是从这个故事开始的,编程课程为了练习for循环,也在不断的练习这个从1加到100的例子,那么原始的办法是这样的: int sum1 = 0; for (int i ...

  3. kali常用配置

    用户须知 1.免责声明:本教程作者及相关参与人员对于任何直接或间接使用本教程内容而导致的任何形式的损失或损害,包括但不限于数据丢失.系统损坏.个人隐私泄露或经济损失等,不承担任何责任.所有使用本教程内 ...

  4. HEOI游记

    \(NOI2024河北省选-HEOI游记\) ·评价 其实作为体验名额,最大的感觉就是自费旅游了一趟. 为什么说是自费呢?下面清点一下账单: 1.两晚酒店 1200 2.KFC 和 拉面 112 3. ...

  5. Java取模和取余,你真的弄懂了吗?

    前言 Java 中常见的取模和取余(求余)计算,在我们日常的很多业务领域都有用到.比如当我们做数据加密时,密码学中不同的加密方案底层会采用不同的模运算来决定其复杂度:做游戏的同学游戏引擎中的取余求最高 ...

  6. 【Mac渗透测试】之SQL注入Demo

    目录: 一.下载安装sqlmap 二.SQL注入 三.参考文章 一.下载安装sqlmap 1.官网地址:http://sqlmap.org/#download git下载: git clone --d ...

  7. 一个操作系统的设计与实现——第19章 IA32-e模式

    19.1 64位段描述符与GDT 在32位操作系统中,我们使用的是平坦模型而非分段模型,从而,段描述符的段基址和段限长均成了摆设.在64位模式下,就连CPU也淘汰了分段模型,转而固定使用平坦模型. 6 ...

  8. pikachu靶场 暴力破解(验证码绕过 on server)

    先随便输入账号和密码.验证码,来判断前端是否对验证码进行判断对错 先随便输入账号和密码不输入验证码,来判断前端是否允许验证码留空 先随便输入账号和密码,输入正确的验证码,来判断账号和密码是否存在 1. ...

  9. 设线性表中每个元素有两个数据项k1和k2,现对线性表按一下规则进行排序:先看数据项k1,k1值小的元素在前,大的在后;在k1值相同的情况下,再看k2,k2值小的在前,大的在后。满足这种要求的

    题目: 设线性表中每个元素有两个数据项k1和k2,现对线性表按一下规则进行排序:先看数据项k1,k1值小的元素在前,大的在后:在k1值相同的情况下,再看k2,k2值小的在前,大的在后.满足这种要求的排 ...

  10. CLion 2022.2.4破解教程详细图解mac,windows,linux均适用

    下载与安装 此教程为CLion 2022.2.4 破解教程,且此教程以及下面提供的破解补丁适用与2022.2以后的新版本. 2022年11月10日亲测有效,mac与windows均测试完美破解 CLi ...