drf中的序列化过程,并且自定义钩子方法来实现特殊功能

98 阅读2分钟

前因: 在drf的序列化过程中,首先加载资源,然后实例化,在我序列化调用data方法的时候,他才会开始序列化,我们进入看看data的调用

@property
def data(self):
    ret = super().data
    return ReturnDict(ret, serializer=self)

接着进入父类的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)
        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):
    """
    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

        # 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

这里的fields主要是获取可以序列化的字段(可读字段)包括read_only=True和啥都没写的 在获取的对象列表中,不仅包括普通的常见的charfield,等还包括SerializerMethodField,而这里面有包括了他的field_name,这个方法相当于是使用instance.字段名称,最后的时候调用to_representation,这里我们来看自定以钩子的细节,不讨论charfield等,下面是自定义钩子的源码

def to_representation(self, value):
    method = getattr(self.parent, self.method_name)
    return method(value)

这个就相当于执行这个方法例如自定义方法的时候会在前面加上个get_,然后是方法名,那么他就会执行这个程序来获取这个值 下面我用一个自定义的钩子方法来实现复合方法,这样既可以触发自己设置的方法,也不会变成必要字段

from collections import OrderedDict

from rest_framework.fields import SkipField
from rest_framework.relations import PKOnlyObject

class NbHookSerializer(object):
    def to_representation(self, instance):

        ret = OrderedDict()
        fields = self._readable_fields

        for field in fields:
            if hasattr(self, 'nb_%s' % field.field_name):
                value = getattr(self, "nb_%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