DRF序列化
从数据库查询出来的都是数据实例对像,不能直接返回客户端,json也并不能序列化数据实例对像,所以需要序列化器,把数据实例对像序列化,表现结果就是把从数据库查询出来的数据,转换成map形式,数据类型保持在数据库中本来的数据类型,返回给客户端
1、创建序列化器
class User(models.Model):
name = models.CharField(max_length=16)
code = models.CharField(max_length=20)
class Meta:
db_table = "users_organizations"
managed = False
from rest_framework import serializers
class userSerialize(serializers.Serializer):
name = serializers.CharField(max_length=100)
code = serializers.CharField(max_length=100)
1.1 创建序列化类
- Serializer是由自定义的SerializerMetaclass 元类来创建的,而userSerialize是继承Serializer,所以userSerialize也是由SerializerMetaclass来创建的
- 元类创建类时会传3个参数,name: 类名 对应 userSerialize ,bases:父类名 对应 Serializer ,attrs:类属性 对应 name 和 code,元类创建的目的是 1、排序,类属性的创建维护了一个计数器,按照计数从小到大排序 。 2、排除不是field的类属性 3、合并父类的field字段 4、修改attrs中的映射,总之是对attrs做处理,之前的形式是 attrs = {‘name’: name_charfield_class_obj, ‘code’: ‘name_charfield_class_obj’} 现在对应的则是 attrs = {‘_declared_fields’: {‘name’: name_charfield_class_obj, ‘code’: ‘name_charfield_class_obj’} }
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)]
# print("fields........", fields)
fields.sort(key=lambda x: x[1]._creation_counter)
# print("fields1.........", fields)
# Ensures a base class field doesn't override cls attrs, and maintains
# field precedence when inheriting multiple parents. e.g. if there is a
# class C(A, B), and A and B both define 'field', use 'field' from A.
known = set(attrs)
# print("known.........",known)
# print(known)
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
]
# print('base_fields.........', base_fields)
return OrderedDict(base_fields + fields)
def __new__(cls, name, bases, attrs):
# print("name..............", name)
# print("bases.............", bases)
# print("attrs..................", attrs)
attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)
return super().__new__(cls, name, bases, attrs)
class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
pass
class userSerialize(serializers.Serializer):
pass
1.2 序列化字段创建
- rest_framework是一个包的文件夹,serializers是一个文件名,CharField这个字段类是来自rest_framework下面的fields文件,之所以可以写成 serializers.CharField,是因为预先把rest_framework.fields的字段名都都导入到 serializers 中了
- 序列化字段的创建就是 创建跟model字段名相同的 实例化Field存在类属性中
1.3 怎么序列化
- 前面强调序列化字段要跟 模型字段同名,因为这地方是要 根据字段名取对应的值 对每个字段名遍历分别取对应的值,
- instance = getattr(instance,attr) 其中instacne 就是数据查询对象,,attr就是我们创建序列化字段的名称
- to_insternal_value 和 to_representation 把对应的值转成字符串
1.4 序列化全流程分析
1.4.1 序列化单条数据时
- Serializer.data 中当调用 super().data时,BaseSerializer.data中的self 也是指的是 当前的 userSerialize的实例化
query_instance = User.objects.filter(code='1682567165964713984').first()
ser_data = userSerialize(instance=query_instance)
# 获取序列化数据
print(ser_data.data)
1.传入instance参数,userSerialize实例化时本身没有自定义__init__方法,Serializer也没有__init__方法,所以继承 BaseSerializer方法
赋值self.instance = instance instance为数据查询实例对象
class BaseSerializer(Field):
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().__init__(**kwargs)
2.调用 ser_data.data. 所以 直接调用Serializer.data
Serializer(BaseSerializer, metaclass=SerializerMetaclass):
@property
def data(self):
# print('self测试。。。。',self)
#当调用super()
ret = super().data
# print('ret测试。。。。',ret)
return ReturnDict(ret, serializer=self)
3. 调用 BaseSerializer.data ,在 self.instance 非空和不报错的情况下,来到 self._data = self.to_representation(self.instance)
self.instance 是数据库查询数据对象
class BaseSerializer(Field):
@property
def data(self):
print('base self....',self)
print('base self....', self.__class__)
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)
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
4.调用 Serializer.to_representation
class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
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)
print()
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)
return ret
5. 创造有序字典 OrderedDict(),OrderedDict()可以记住元素的插入顺序
6. 获取fields
class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
@property
def _readable_fields(self):
for field in self.fields.values():
if not field.write_only:
yield field
7.self.fields的来源
class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
@cached_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.
fields = BindingDict(self)
for key, value in self.get_fields().items():
fields[key] = value
return fields
7. 来到 fields = BindingDict(self)
1、fields = BindingDict(self) self指的是 自己写的序列化类的实例化对象 返回的fields 是 BindingDict类的实例化
2、self.serializer = serializer serializer 指的是1的self
3、后面迭代 BindingDict时,走的是 __getitem__ 函数
class BindingDict(MutableMapping):
"""
This dict-like object is used to store fields on a serializer.
This ensures that whenever fields are added to the serializer we call
`field.bind()` so that the `field_name` and `parent` attributes
can be set correctly.
"""
def __init__(self, serializer):
self.serializer = serializer
self.fields = OrderedDict()
def __setitem__(self, key, field):
self.fields[key] = field
field.bind(field_name=key, parent=self.serializer)
def __getitem__(self, key):
return self.fields[key]
def __delitem__(self, key):
del self.fields[key]
def __iter__(self):
return iter(self.fields)
def __len__(self):
return len(self.fields)
def __repr__(self):
return dict.__repr__(self.fields)
9. self.get_fields 中获取fields,从 _declared_fields中获取 fileds,就像如上所说的attrs['_declared_fields'] = {'name':name_filed_class_instance,'code':code_field_class_instacne},表现就是 _declared_fields = {'name':name_filed_class_instance,'code':code_field_class_instacne}, _declared_fields 就是类属性
self.get_fields 返回 _declared_fields 的深复制,
class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
def get_fields(self):
"""
Returns a dictionary of {field_name: field_instance}.
"""
# Every new serializer is created with a clone of the field instances.
# This allows users to dynamically modify the fields on a serializer
# instance without affecting every other serializer instance.
return copy.deepcopy(self._declared_fields)
9.继续回到Serializer.to_representation,
1.fields是一个生成器,返回是字段的实例化类
2. for field in fields: 字段实例化类
3. instance
class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
def to_representation(self, instance):
"""
Object instance -> Dict of primitive datatypes.
"""
ret = OrderedDict()
# fields是一个生成器,返回是字段的实例化类
fields = self._readable_fields
# 返回字段实例化类
for field in fields:
try:
attribute = field.get_attribute(instance)
print()
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)
return ret
10. 调用field.get_attribute,以Charfield为例
self.source_attrs 一般情况下就是 字段的名称,source_attrs在bind被赋予值
def bind(self, field_name, parent):
"""
Initializes the field name and parent for the field instance.
Called when a field is added to the parent serializer instance.
"""
class CharField(Field):
pass
class Field:
def get_attribute(self, instance):
"""
Given the *outgoing* object instance, return the primitive value
that should be used for this field.
"""
print("field测试..........",self)
try:
print('field测试111.......',self.source_attrs)
return get_attribute(instance, self.source_attrs)
except BuiltinSignatureError as exc:
pass
11. 调用 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:
try:
if isinstance(instance, Mapping):
instance = instance[attr]
else:
instance = getattr(instance, attr)
print('ces.....',instance,type(instance))
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 "{}"; original exception was: {}'.format(attr, exc))
return instance
12. 继续回到 Serializer.to_representation
class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
def to_representation(self, instance):
"""
Object instance -> Dict of primitive datatypes.
"""
ret = OrderedDict()
# fields是一个生成器,返回是字段的实例化类
fields = self._readable_fields
# 返回字段实例化类
for field in fields:
try:
# 对应的字段值
attribute = field.get_attribute(instance)
print()
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是一个有序字典,转换attribute的值为对应的字段类型,把字段的值都加到了ret中
ret[field.field_name] = field.to_representation(attribute)
return ret
13. 调用 field.to_representation,
class CharField(Field):
def to_representation(self, value):
return str(value)
1.4.2 序列化多条数据时 ,序列化数据时 many=True
query_instance = User.objects.filter(code__in=['11','22']).all()
ser_data = userSerialize(instance=query_instance,many=True)
# 获取序列化数据
print(ser_data.data)
1.序列化userSerialize,返回ListSerializer
1.当实例化序列化类 userSerialize时,首先会走父类的__new__方法
2.当many的参数为True时,那么不再返回userSerialize的 实例化类,而是去到 cls.many_init(*args, **kwargs)函数
3.child_serializer = cls(*args, **kwargs) 中cls指的是 userSerialize 序列化类
3.返回ListSerializer的实例化,list_kwargs中包含userSerialize 传给了ListSerializer
class BaseSerializer(Field):
def __new__(cls, *args, **kwargs):
# We override this method in order to automatically create
# `ListSerializer` classes instead when `many=True` is set.
print('base cls........', cls)
if kwargs.pop('many', False):
print('base cls_cls_cls........',cls)
return cls.many_init(*args, **kwargs)
return super().__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)
max_length = kwargs.pop('max_length', None)
min_length = kwargs.pop('min_length', None)
child_serializer = cls(*args, **kwargs)
list_kwargs = {
'child': child_serializer,
}
if allow_empty is not None:
list_kwargs['allow_empty'] = allow_empty
if max_length is not None:
list_kwargs['max_length'] = max_length
if min_length is not None:
list_kwargs['min_length'] = min_length
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)
2. ListSerializer的实例化
1.实例化时传入的参数是 list_kwargs
2. super().__init__(*args, **kwargs) 接收 instance参数 self.instance = instance
class ListSerializer(BaseSerializer):
child = None
many = True
default_error_messages = {
'not_a_list': _('Expected a list of items but got type "{input_type}".'),
'empty': _('This list may not be empty.'),
'max_length': _('Ensure this field has no more than {max_length} elements.'),
'min_length': _('Ensure this field has at least {min_length} elements.')
}
def __init__(self, *args, **kwargs):
self.child = kwargs.pop('child', copy.deepcopy(self.child))
self.allow_empty = kwargs.pop('allow_empty', True)
self.max_length = kwargs.pop('max_length', None)
self.min_length = kwargs.pop('min_length', None)
assert self.child is not None, '`child` is a required argument.'
# 检查self.child是否是类 , inspect.isclass确定某个对像是否为类
assert not inspect.isclass(self.child), '`child` has not been instantiated.'
super().__init__(*args, **kwargs)
self.child.bind(field_name='', parent=self)
3.调用 ser_data.data ,ser_data指的就是 ListSerializer 了,来到
class ListSerializer(BaseSerializer):
@property
def data(self):
ret = super().data
return ReturnList(ret, serializer=self)
4. 调用 BaseSerializer.data
class BaseSerializer(Field):
@property
def data(self):
print('base self....',self)
print('base self....', self.__class__)
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)
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
5.调用 ListSerializer.to_representation
1.data是多个查询对象
2. self.child.to_representation(item) 跟单条数据序列化的过程是一样的
class ListSerializer(BaseSerializer):
def to_representation(self, data):
"""
List of object instances -> List of dicts of primitive datatypes.
"""
# Dealing with nested relationships, data can be a Manager,
# so, first get a queryset from the Manager if needed
iterable = data.all() if isinstance(data, models.Manager) else data
return [
self.child.to_representation(item) for item in iterable
]
2、SerializerMethodField
2.1 使用
class User(models.Model):
name = models.CharField(max_length=16)
code = models.CharField(max_length=20)
class Meta:
db_table = "users_organizations"
managed = False
from rest_framework import serializers
class userSerialize(serializers.Serializer):
name = serializers.CharField(max_length=100)
code = serializers.SerializerMethodField()
def get_code(instance):
return instance.code
2.2 源码分析
1. SerializerMethodField 分析:
1.初始化SerializerMethodField时, kwargs['source'] = '*',这样在调用 super().__init__(**kwargs)时,source的值为'*'
2.如果source的值为'*',那么在bind这步的时候 self.source_attrs = []
3.所以在这步 attribute = field.get_attribute(instance)时,返回的仍是instacne本身,而不是instacne的属性值
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
kwargs['source'] = '*'
kwargs['read_only'] = True
super().__init__(**kwargs)
def bind(self, field_name, parent):
# The method name defaults to `get_{field_name}`.
if self.method_name is None:
self.method_name = 'get_{field_name}'.format(field_name=field_name)
super().bind(field_name, parent)
def to_representation(self, value):
method = getattr(self.parent, self.method_name)
return method(value)
class Field:
def bind(self, field_name, parent):
"""
Initializes the field name and parent for the field instance.
Called when a field is added to the parent serializer instance.
"""
# In order to enforce a consistent style, we error if a redundant
# 'source' argument has been used. For example:
# my_field = serializer.CharField(source='my_field')
assert self.source != field_name, (
"It is redundant to specify `source='%s'` on field '%s' in "
"serializer '%s', because it is the same as the field name. "
"Remove the `source` keyword argument." %
(field_name, self.__class__.__name__, parent.__class__.__name__)
)
self.field_name = field_name
self.parent = parent
# `self.label` should default to being based on the field name.
if self.label is None:
self.label = field_name.replace('_', ' ').capitalize()
# self.source should default to being the same as the field name.
if self.source is None:
self.source = field_name
# self.source_attrs is a list of attributes that need to be looked up
# when serializing the instance, or populating the validated data.
if self.source == '*':
self.source_attrs = []
else:
self.source_attrs = self.source.split('.')
当我们使用 SerializerMethodField的时候,接着1.4.1 的第9步分析
2.调用Serializer.to_representation
1.attribute = field.get_attribute(instance)返回的值仍然是instance本身,不是instance的属性值
2. ret[field.field_name] = field.to_representation(attribute) 调用 field的 to_representation的值
class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
def to_representation(self, instance):
"""
Object instance -> Dict of primitive datatypes.
"""
ret = OrderedDict()
# fields是一个生成器,返回是字段的实例化类
fields = self._readable_fields
# 返回字段实例化类
for field in fields:
try:
attribute = field.get_attribute(instance)
print()
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)
return ret
3. 调用SerializerMethodField.to_representation
1.从userSerialize 序列化类中获取 get_code方法
2.value就是 instance(数据库查询实例对象本身)
3.返回对应的值
class SerializerMethodField(Field):
def to_representation(self, value):
method = getattr(self.parent, self.method_name)
return method(value)