DRF:序列化和反序列化
- 请求过来的json需要转换为python中的对象
- python中的对象需要转换为json作为响应
场景:
- 请求(json/form) ——> 反序列化 ——> python可以操作的对象
- orm: sql执行结果->queryset(python对象)
- queryset(py) --> 序列化 --> json
本文目的: 探索一下drf序列化的流程.
- Validator(验证器): rest_framework.validators.py * *
- Field(字段): rest_framework.fields.py *
- Serializer(序列化类): rest_framework.serializers.py
- 验证字段合法
- 验证业务逻辑合法
- Serializer 和 Field之前调用关系
- 自定义Field
1.Validator
2.Field
- Field(object): 字段类
- BooleanField(Field)
- CharField(Field)
- ChoiceField(Field)
- DateField(Field)
- DateTimeField(Field)
- DecimalField(Field)
- DictField(Field)
- DurationField(Field)
- EmailField(CharField): 邮件字段
- RegexField(CharField): 正则
- FileField(Field): 文件
- FilePathField(ChoiceField)
- FloatField(Field)
- HiddenField(Field)
- ImageField(FileField)
- IntegerField(Field)
- IPAddressField(CharField)
- JSONField(Field)
- ListField(Field)
- ModelField(Field)
- MultipleChoiceField(ChoiceField)
- NullBooleanField(Field) *
- ReadOnlyField(Field):
- init: kwargs['read_only'] = True
- to_representation: py -> response
1.1 分析NullBooleanField
- init: 设置allow_null为True
- to_internal_value: 把value转为py对象True,False,None
- to_representation:
class NullBooleanField(Field):
default_error_messages = {
'invalid': _('"{input}" is not a valid boolean.')
}
initial = None
TRUE_VALUES = {'t', 'T', 'true', 'True', 'TRUE', '1', 1, True}
FALSE_VALUES = {'f', 'F', 'false', 'False', 'FALSE', '0', 0, 0.0, False}
NULL_VALUES = {'n', 'N', 'null', 'Null', 'NULL', '', None}
def __init__(self, **kwargs):
assert 'allow_null' not in kwargs, '`allow_null` is not a valid option.'
kwargs['allow_null'] = True
super(NullBooleanField, self).__init__(**kwargs)
def to_internal_value(self, data):
if data in self.TRUE_VALUES:
return True
elif data in self.FALSE_VALUES:
return False
elif data in self.NULL_VALUES:
return None
self.fail('invalid', input=data)
def to_representation(self, value):
if value in self.NULL_VALUES:
return None
if value in self.TRUE_VALUES:
return True
elif value in self.FALSE_VALUES:
return False
return bool(value)
3.Serializer
app.serializers.py
class TestModelSerializer(BaseSerializer):
"""
TestModel Serializer
"""
currentPage = IntegerWithNullField(
required=False,
help_text=u'当前页数(从1开始取值),当method=page 时必传')
def validate(self, attrs):
# 业务逻辑是否合法
return attrs
views.py
class APIPullProductInfoSView(AllowAnyView):
"""
APIPullProductInfoSView
"""
serializer_class = APIPullProductInfoSerializer
def get(self, request):
"""
---
"""
logger.info("request.data:{}".format(request.data))
serializer = self.get_serializer(data=request.data)
if not serializer.is_valid():
logger.debug('serializer err:{}'.format(serializer.errors))
return Response(
serializer.errors, status=status.HTTP_400_BAD_REQUEST)
validated_data = serializer.validated_data
return Response(validated_data)
1.Serializer触发流程
is_valid调用前:
- new
- init *
- many_init
is_valid 调用时:
- run_validation
- to_internal_value
is_valid调用后:
- to_representation
rest_framework.serializers.BaseSerializer
class BaseSerializer(Field):
def __init__(self, instance=None, data=empty, **kwargs):
# 绑定model instantce
self.instance = instance
if data is not empty:
# 绑定数据到initial_data
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)
@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)
child_serializer = cls(*args, **kwargs)
list_kwargs = {
'child': child_serializer,
}
if allow_empty is not None:
list_kwargs['allow_empty'] = allow_empty
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)
def to_internal_value(self, data):
raise NotImplementedError('`to_internal_value()` must be implemented.')
def to_representation(self, instance):
raise NotImplementedError('`to_representation()` must be implemented.')
def update(self, instance, validated_data):
raise NotImplementedError('`update()` must be implemented.')
def create(self, validated_data):
raise NotImplementedError('`create()` must be implemented.')
def save(self, **kwargs):
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
def is_valid(self, raise_exception=False):
# 调用者触发.
if not hasattr(self, '_validated_data'):
try:
# 调用self.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)
@property
def data(self):
# 获取序列化器的数据
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
@property
def errors(self):
# 获取错误...
return self._errors
@property
def validated_data(self):
# 获取验证后的数据...
return self._validated_data
2.Serializer: 实现run_validation
- run_validation: 运行序列化器的验证器
- self.validate_empty_values(data)
- value = self.to_internal_value(data):
- 遍历序列化器类的所有字段: for field in fields:(该Field可以嵌套Serializer)
- 先获取该Field的值,primitive_value = field.get_value(data)
- 运行字段的验证器validated_value = field.run_validation(primitive_value)
- (is_empty_value, data) = filed.validate_empty_values(data)
- value = filed.to_internal_value(data)
- self.run_validators(value)
- 最大值验证
- 最小值验证
- 正则验证...
- 自定义字段验证方法:validate_method: validate_field.field_name
- validated_value = validate_method(validated_value)
- 遍历序列化器类的所有字段: for field in fields:(该Field可以嵌套Serializer)
- self.run_validators(value): 检查该序列化器类中所有的字段都符合要求
- value = self.validate(value): 整个输入对象,是否符合要求.serializer.validated_data
@six.add_metaclass(SerializerMetaclass)
class Serializer(BaseSerializer):
default_error_messages = {
'invalid': _('Invalid data. Expected a dictionary, but got {datatype}.')
}
def is_valid(self, raise_exception=False):
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)
def run_validation(self, data=empty):
# 1.验证是否为空
(is_empty_value, data) = self.validate_empty_values(data)
if is_empty_value:
return data
# 2.输入转为python对象. 对所有field进行验证,失败抛出异常
value = self.to_internal_value(data)
try:
# 3.
self.run_validators(value)
# 4. 序列化的validate方法, 所有filed已经验证完毕.
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 to_internal_value(self, data):
if not isinstance(data, dict):
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
# fields: 当前序列化类的可写字段
for field in fields:
# field: 是CharField/InterField层,或者是嵌套的序列化类
validate_method = getattr(self, 'validate_' + field.field_name, None)
# 1.field.get_value 获取Field().get_value()
primitive_value = field.get_value(data)
try:
# 2.调用Field字段的run_validation
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
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)
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
def validate(self, attrs):
"""
到这里时,所有字段已经合法,进行下一步业务逻辑验证.
"""
return attrs
3.Serializer 和 Field衔接入口
序列化类to_internal_value中编译每一个field字段是,调用Field.run_validation
- 序列化类.run_validation:
- Field.validate_empty_values
- Field.to_internal_value
- Field.run_validators(value)
class Field(object):
def get_value(self, dictionary):
if html.is_html_input(dictionary):
# HTML forms will represent empty fields as '', and cannot
# represent None or False values directly.
if self.field_name not in dictionary:
if getattr(self.root, 'partial', False):
return empty
return self.default_empty_html
ret = dictionary[self.field_name]
if ret == '' and self.allow_null:
# If the field is blank, and null is a valid value then
# determine if we should use null instead.
return '' if getattr(self, 'allow_blank', False) else None
elif ret == '' and not self.required:
# If the field is blank, and emptiness is valid then
# determine if we should use emptiness instead.
return '' if getattr(self, 'allow_blank', False) else empty
return ret
return dictionary.get(self.field_name, empty)
def run_validation(self, data=empty):
# 1.验证空值
(is_empty_value, data) = self.validate_empty_values(data)
if is_empty_value:
return data
# 2.调用Field的to_internal_value
value = self.to_internal_value(data)
# 3.运行Field的验证器
self.run_validators(value)
return value
def run_validators(self, value):
errors = []
for validator in self.validators:
# 遍历Field的所有验证器.
if hasattr(validator, 'set_context'):
validator.set_context(self)
try:
# 调用验证器类,对输入值进行验证
validator(value)
except ValidationError as exc:
if isinstance(exc.detail, dict):
raise
errors.extend(exc.detail)
except DjangoValidationError as exc:
errors.extend(get_error_detail(exc))
if errors:
raise ValidationError(errors)