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)