Django Rest Framework 源码解析--序列化

Django Rest Framework 源码解析--序列化

示例代码就只展示了后端编写的代码和序列化过程,示例代码如下:
懒得分文件就全部写再views.py中了

import re
 
from django.db import models
 
from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
 
 
class UserProfile(models.Model):
    """
    用户表
    """
    username = models.CharField(
        max_length=20, default="", verbose_name="姓名", help_text="姓名")
    email = models.EmailField(
        max_length=50, verbose_name="邮箱", help_text="邮箱")
 
 
class UserSerializer(serializers.ModelSerializer):
    """
    用户序列化
    """
    class Meta:
        model = UserProfile
        fields = "__all__"
 
 
class UserViewSet(ModelViewSet):
    """
    用户管理:增删改查
    """
    queryset = UserProfile.objects.all()
    serializer_class = UserSerializer

url.py

from django.contrib import admin
from django.urls import path, include
 
from study.views import UserViewSet
from rest_framework import routers
 
router = routers.SimpleRouter()
router.register(r"users", UserViewSet, base_name="users")
 
urlpatterns = [
    path('admin/', admin.site.urls),
    path(r"api/", include(router.urls))

一、查看用户列表Serializer的序列化过程

1、获取序列化对象

class ListModelMixin:
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
 
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
        # 获取序列化对象
        # 这里面的传参要说明一点data有值就是反序列化,instance有值就是序列化
        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)
class GenericAPIView(views.APIView):
    .......
    serializer_class = None
    .......
    def get_serializer(self, *args, **kwargs):
        """
        Return the serializer instance that should be used for validating and
        deserializing input, and for serializing output.
        """
        # 获取序列化对象
        serializer_class = self.get_serializer_class()
        kwargs['context'] = self.get_serializer_context()
        return serializer_class(*args, **kwargs)
 
    def get_serializer_class(self):
        """
        Return the class to use for the serializer.
        Defaults to using `self.serializer_class`.
        You may want to override this if you need to provide different
        serializations depending on the incoming request.
        (Eg. admins get full serialization, others get basic serialization)
        """
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer_class()` method."
            % self.__class__.__name__
        )
        # 返回的就是我们再Views定义的serializer_class = UserSerializer
        return self.serializer_class
    ......

通过上一步我们了解到serializer =self.get_serializer(queryset, many=True) 执行的UserSerializer类的实例化
2、UserSerializer类的实例化的过程
类实例化之前会执行new方法,用于控制一个类的生成实例的过程生成一个空对象,子类没有的就去找父类的new, new执行完以后才能执行init构造方法
UserSerializer的父类ModelSerializer没有new方法,ModelSerializer的父类Serializer也没有new方法,在往上找BaseSerlizer中的new方法

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)
 
    def __new__(cls, *args, **kwargs):
        # We override this method in order to automagically create
        # `ListSerializer` classes instead when `many=True` is set.
        
        # 传入的参数是many=True,执行cls.many_init(*args, **kwargs)
        if kwargs.pop('many', False):
            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)
        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)
        # 调用ListSerializer
        list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
        return list_serializer_class(*args, **list_kwargs)
      # 后面就是执行各个类的init构造方法

3、UserSerializer类的实例化后执行return Response(serializer.data)

class ListSerializer(BaseSerializer):
    ......
    @property
    def data(self):
        # 执行父类的data
        ret = super().data
        return ReturnList(ret, serializer=self)
      
class BaseSerializer(Field):
    ......
    @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.'
            )
            # 如果要访问data属性必须先调用is_valid方法进行检查
            raise AssertionError(msg)
        # 如果没有_data属性
        if not hasattr(self, '_data'):
            # 实例不为空并且没有_errors属性
            if self.instance is not None and not getattr(self, '_errors', None):
                self._data = self.to_representation(self.instance)
            # 如果有is_valid后的数据并没有检查出错误则调用to_representation处理
            elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None):
                self._data = self.to_representation(self.validated_data)
            else:
                # 如果都不符合则调用get_initial处理
                self._data = self.get_initial()
        return self._data

4、由于我们传入的instance,在执行self.to_representation函数时,就传入了instance实例

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:
                # 获取实例中对应的field字段
                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:
                # 否则调用field的to_representation来处理attribute
                ret[field.field_name] = field.to_representation(attribute)
 
        return ret
@property
def _readable_fields(self):
    # 在初始化的时候就获取只读的字段值
    for field in self.fields.values():
        if not field.write_only:
            yield field
@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)
    # 调用get_fields获取字段属性值
    for key, value in self.get_fields().items():
        # 写入该值
        fields[key] = value
    # 返回该字段
    return fields
class ModelSerializer(Serializer):
    ......
    def get_fields(self):
        """
        Return the dict of field names -> field instances that should be
        used for `self.fields` when instantiating the serializer.
        """
        # 检查url_field_name
        if self.url_field_name is None:
            self.url_field_name = api_settings.URL_FIELD_NAME
        # 必须配置Meta属性
        assert hasattr(self, 'Meta'), (
            'Class {serializer_class} missing "Meta" attribute'.format(
                serializer_class=self.__class__.__name__
            )
        )
        # 必须在Meta中配置model属性
        assert hasattr(self.Meta, 'model'), (
            'Class {serializer_class} missing "Meta.model" attribute'.format(
                serializer_class=self.__class__.__name__
            )
        )
        # 如果是抽象则直接报错
        if model_meta.is_abstract_model(self.Meta.model):
            raise ValueError(
                'Cannot use ModelSerializer with Abstract Models.'
            )
        # 深拷贝所有字段
        declared_fields = copy.deepcopy(self._declared_fields)
        # 获取model
        model = getattr(self.Meta, 'model')
        # 获取深度信息
        depth = getattr(self.Meta, 'depth', 0)
        # 深度必须大于等于0小于等于10
        if depth is not None:
            assert depth >= 0, "'depth' may not be negative."
            assert depth <= 10, "'depth' may not be greater than 10."
 
        # Retrieve metadata about fields & relationships on the model class.
        # 获取model的信息
        info = model_meta.get_field_info(model)
        # 获取filed字段名称
        field_names = self.get_field_names(declared_fields, info)
 
        # Determine any extra field arguments and hidden fields that
        # should be included
        # 获取额外参数
        extra_kwargs = self.get_extra_kwargs()
        extra_kwargs, hidden_fields = self.get_uniqueness_extra_kwargs(
            field_names, declared_fields, extra_kwargs
        )
 
        # Determine the fields that should be included on the serializer.
        fields = OrderedDict()
        # 遍历字段名称
        for field_name in field_names:
            # If the field is explicitly declared on the class then use that.
            # 如果在初始化的字段中
            if field_name in declared_fields:
                # 直接设置比进行下一个
                fields[field_name] = declared_fields[field_name]
                continue
                    
            # 获取额外定义的字段
            extra_field_kwargs = extra_kwargs.get(field_name, {})
            source = extra_field_kwargs.get('source', '*')
            if source == '*':
                source = field_name
 
            # Determine the serializer field class and keyword arguments.
            # 确定序列化器字段类和关键字参数
            field_class, field_kwargs = self.build_field(
                source, info, model, depth
            )
 
            # Include any kwargs defined in `Meta.extra_kwargs`
            field_kwargs = self.include_extra_kwargs(
                field_kwargs, extra_field_kwargs
            )
 
            # Create the serializer field.
            # 创建额外字段的field实例
            fields[field_name] = field_class(**field_kwargs)
 
        # Add in any hidden fields.
        fields.update(hidden_fields)
 
        return fields

此时就通过Model转换成了序列化中渲染的字段值,在获取属性的过程中,其中filed.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:
            # 检查是否为Mapping
            if isinstance(instance, Mapping):
                # 直接获取属性
                instance = instance[attr]
            else:
                # 直接获取实例的该属性
                instance = getattr(instance, attr)
        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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容

  • Django Rest Framework 源码解析--序列化之反序列化 示例代码就只展示了后端编写的代码和序列化...
    叶_叶阅读 549评论 0 0
  • 介绍 本教程将涵盖一个简单的PasteBin1代码高亮的Web API。整个过程,将逐一介绍REST framew...
    盛夏_264f阅读 522评论 0 0
  • JAVA序列化机制的深入研究 对象序列化的最主要的用处就是在传递,和保存对象(object)的时候,保证对象的完整...
    时待吾阅读 10,859评论 0 24
  • Serializers 序列化器允许将诸如查询集和模型实例之类的复杂数据转换为原生 Python 数据类型,然后可...
    lkning阅读 1,023评论 0 1
  • 今天一群人到山谷里的豆豆家给龙哥过生日,热热闹闹的聊天吃零食做饭。久违了的家的温暖。真好。 想给家里收拾一下,收拾...
    一般小凉阅读 462评论 0 0