您现在的位置是:首页 > 正文

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

2024-04-01 01:52:43阅读 2

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

 

网站文章

  • DoIP学习笔记系列:(二)VN5620 DoIP测试配置实践笔记

    DoIP学习笔记系列:(二)VN5620 DoIP测试配置实践笔记

    VN5620 DoIP测试配置实践笔记

    2024-04-01 01:52:12
  • 5. JVM 性能优化

    5. JVM 性能优化

    1. 内存溢出 内存溢出的方式: 栈溢出 堆溢出 方法区溢出 本级内存直接溢出 1.1 栈溢出 StackOverflowError: 方法自己调用自己,死递归导致的溢出。 OutOfMemoryEr...

    2024-04-01 01:52:07
  • 【审稿快刊】计算机科学类SCI,仅2-3个月左右录用,涵盖领域广

    计算机科学和控制系统相关问题的广泛领域,包括:软硬件工程;【期刊简介】IF:2.5-3.0,JCR2/3区,中科院4区。【期刊简介】IF:3.0-4.0,JCR2/3区,中科院4区。【期刊简介】IF:...

    2024-04-01 01:51:40
  • IIS 查看日志

    IIS 查看日志

    2、IIS查看日志:   在发布服务的过程中,可能会出现错误,需要用日志定位。怎么查看IIS的日志呢? (1)首先,在IIS的网站功能视图中,可以找到一个日志功能按钮 (2)点击进入后,发现并不是日志本身,而是对IIS中日志的一些配置   其中比较常用的,需要知道的就是日志存放目录,以及W3C格式 (3)找到路径”%SystemDrive%\inetpub\logs\LogFi...

    2024-04-01 01:51:33
  • linux提权(find、CVE-2017-1699、dirtycow)

    linux提权(find、CVE-2017-1699、dirtycow)

    linux提权脏牛提权CVE-2017-1699find提权

    2024-04-01 01:51:27
  • html 文字不可选择,css文字不可选怎么做?

    html 文字不可选择,css文字不可选怎么做?

    css设置文字不可选使用user-select属性,user-select属性设置或检索是否允许用户选中文本,下面我们来看一下使用user-select属性设置文字不可选的方法。css设置文字不可选示...

    2024-04-01 01:51:02
  • WTM+LayUI 子表批量上传文件

    WTM+LayUI 子表批量上传文件

    一个带有的Model,实现批量新增。

    2024-04-01 01:50:56
  • 使用PyQt5创建和编写.qrc资源文件

    在PyQt5中,可以使用.qrc资源文件来管理应用程序中的来管理应用程序中的静态资源,如图像、样式表和其他文件。一旦创建和编写.qrc文件,我们需要将其编译为Python代码,以便在应用程序来管理应用...

    2024-04-01 01:50:50
  • 全面盘点‘’一网打尽‘’,架构师的必备技能(微服务、高并发、大数据、缓存等中间件)是如何炼成的?

    全面盘点‘’一网打尽‘’,架构师的必备技能(微服务、高并发、大数据、缓存等中间件)是如何炼成的?

    现代的互联网体系结构面临着异常庞杂的服务拓扑,如何合理地进行服务治理是架构师领域核心的一个命题。业务领域、基础架构领域、组织结构领域,如何做服务治理?服务治理是如何一步步演变进化的?我们未来又将面临哪...

    2024-04-01 01:50:42
  • 用单例封装 SharedPreferences

    自己封装的 SharedPreferences,很简单的封装,直接上代码public class SPManager { private static final String ACCOUNT = "account"; private static final String PASSWORD = "password"; private st

    2024-04-01 01:50:17