1. Source code analysis
Note: The following code snippet has been simplified for ease of understanding, keeping only the code related to the serialization function
The source code for serialization involves the concept of metaclasses, which I'll briefly explain here: a metaclass is a high-level concept used to define the behavior of class creation. Simply put, a metaclass is a class that creates a class, and it determines how the class is created and how it behaves.
Everything is an object in Python, including classes. Every class has a metaclass that defines how the class is created. Typically, Python creates classes using the default metaclass type. However, you can use metaclasses when you want to customize the class creation process, for example:
class Mytype(type)
def __new__(cls,name,bases,attrs): # Class name, inherited parent class, members.
# Here you can perform operations on the class you want to create.
del attrs["v1"]
attrs["name"] = "harry"
xx = super(). __new__(cls,name,bases,attrs) # call type class to create object (this object is the Bar class)
retyrn xx
class Bar(object, metaclass=Mytype) # metaclass specifies a custom metaclass
v1 = 123
def func(self).
pass
Since the v1 attribute was removed from the metaclass and the name attribute was added, there is no v1 attribute in Bar and there is a name attribute.
Another:parent classIf the metaclass metaclass is specified, itssubcategories(not) at alldefault (setting)is used to create the class with that metaclass
Addendum: Instantiating the Bar class is equivalent to a type object (), and therefore triggers the __call__ method of the type class, which calls Bar's __new__ and __init__, and that's why the __new__ and __init__ methods of the class are automatically triggered when the class is instantiated. Essentially the call methods of the type metaclass are called because of Object();
The Serializers component has two main functions: serialization and data validation.
- Serialization section:
First define a serialization class
class DepartSerializer():
'''Serializercalibration'''
# 内置calibration
title = (required=True, max_length=20, min_length=6)
order = (required=False, max_value=100, min_value=10)
count = (choices=[(1, "high level"), (2, "middle level (in a hierarchy)")])
Looking at the parent class of Serializer, it can be seen that it was created via the SerializerMetaclass metaclass
Serializer(BaseSerializer, metaclass=SerializerMetaclass)
SerializerMetaclass metaclass source code:
class SerializerMetaclass(type):
@classmethod
def _get_declared_fields(cls, bases, attrs):
fields = [(field_name, (field_name)) # Fetch by loopfieldfield object (computing)
for field_name, obj in list(())
if isinstance(obj, Field)]
(key=lambda x: x[1]._creation_counter)
known = set(attrs)
def visit(name):
(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) # Added to the class_declared_fieldscausality,which encapsulates all theFieldField name and corresponding object
return super().__new__(cls, name, bases, attrs)
by triggering the serialization process:
@property
def data(self):
ret = super().data # Finding its parent classBaseSerializer(used form a nominal expression)datamethodologies
return ReturnDict(ret, serializer=self)
Source code for BaseSerializer's data method:
@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 is not None and not getattr(self, '_errors', None):
self._data = self.to_representation() # fulfillmentto_representationmethod to get the serialized data
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 method source code (core):
def to_representation(self, instance):
ret = OrderedDict()
fields = self._readable_fields # Filter out readable field objects(its internal control over_declared_fieldsFields are deep-copied)
for field in fields:
try:
attribute = field.get_attribute(instance) # Cyclic field object list,and implementationget_attributemethod to get the corresponding value
except SkipField:
continue
check_for_none = 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) # fulfillmentto_representationconversion format,and encapsulates all the data in theretdictionary
return ret
Source code for the get_attribute method:
def get_attribute(self, instance):
return get_attribute(instance, self.source_attrs)
def get_attribute(instance, attrs): # attrsbecause ofsourcefield value instancebecause of模型对象
for attr in attrs:
try:
if isinstance(instance, Mapping):
instance = instance[attr]
else:
instance = getattr(instance, attr) # Loop to get the model object's finalattrvalue of
except ObjectDoesNotExist:
return None
return instance # 返回该field value
2. Data validation component
Use the is_valid method to validate the data, get the _errors data, and is_valid returns False if _errors exists. run_validation method is triggered during the execution of this function:
def is_valid(self, raise_exception=False):
if not hasattr(self, '_validated_data'):
try: # It's triggered.run_validationmethodologies
self._validated_data = self.run_validation(self.initial_data)
except ValidationError as exc:
self._validated_data = {}
self._errors =
else:
self._errors = {}
if self._errors and raise_exception:
raise ValidationError()
return not bool(self._errors)****
run_validation method, note that this method is a method under the Serializer class, not the Field class. Call the field's built-in validation in the to_internal_value method and execute the hook function.
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) # Calling fields with built-in checks,and execute the hook function
try:
self.run_validators(value)
value = (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
The to_internal_value method, where fileds are deep-copied from _declared_fields, contains only write-only field objects.
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 # Filtering write-only field objects
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) # Perform built-in calibration
if validate_method is not None:
validated_value = validate_method(validated_value) # Execute the hook function to perform the checksum
except ValidationError as exc:
errors[field.field_name] =
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 has built-in calibration:
def run_validation(self, data=empty):
if data == '' or (self.trim_whitespace and str(data).strip() == ''):
if not self.allow_blank:
('blank')
return ''
return super().run_validation(data)
# parentrun_validationmethodologies
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) # Calling the field definition of therun_validatorscarry out calibration
return value
2. Source code adaptation:
- Custom hooks: allow a field to both support front-end pass-in and customize the value returned by serialization; (SerializerMethodField is read-only by default, so the user can't enter it, and regular fields can't be customized to return values with complex logic)
Idea: determine if there is a hook for custom formatting in the to_representation method after the call to start serialization, and if there is, replace the value of the field object
def to_representation(self, instance):
ret = OrderedDict()
fields = self._readable_fields
for field in fields:
if hasattr(self, 'get_%s' % field.field_name): # Determine if there is a"get_xxx"functions of the form,If then the method is executed and theinstancetransmitted inwards
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 = 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
If you need to use this rewrite method in other classes, you can encapsulate the rewrite method into a class, so that you don't have to rewrite the to_representation method every time you inherit from the other classes.