DRF的Serializer组件(源码分析)

news2025/1/12 4:42:52

1. 数据校验

drf中为我们提供了Serializer,他主要有两大功能:

  • 对请求数据校验(底层调用Django的Form和ModelForm)
  • 对数据库查询到的对象进行序列化

示例一: 基于Serializer

# models.py
class UserInfo(models.Model):
    username = models.CharField(verbose_name='用户名', max_length=32)
    age = models.CharField(verbose_name='年龄', max_length=32)
    level_choice = ((1, 'VIP'), (2, 'SVIP'), (3, 'PARTNER'))
    level = models.CharField(verbose_name='级别', choices=level_choice, max_length=32)
    email = models.CharField(verbose_name='邮箱', max_length=32)
# views.py 基于Serializer
class UserSerializers(serializers.Serializer):
    username = serializers.CharField(label='用户名', max_length=32)
    age = serializers.CharField(label='年龄', max_length=32)
    level = serializers.ChoiceField(label='级别', choices=models.UserInfo.level_choice)
    email = serializers.CharField(label='用户名', min_length=6, max_length=32, validators=[EmailValidator, ])
    email1 = serializers.CharField(label='用户名', min_length=6, max_length=32)
    email2 = serializers.CharField(label='用户名', min_length=6, max_length=32)

    def validate_email2(self, value):
        """ 钩子函数, 用于验证某个字段 """
        if re.match('^\w+@\w+\.\w+$', value):
            return value
        raise exceptions.ValidationError('邮箱格式错误')
 
class UserView(APIView):
    """ 用户管理 """

    def post(self, request):
        """ 添加用户 """
        ser = UserSerializers(data=request.data)  # 将请求体数据传入, 这个request.data可以解析各种数据
        if not ser.is_valid():
            return Response({'code': 1006, 'data': ser.errors})
        print(ser.validated_data)

        # 将数据保存到数据库
        return Response({'code': 0, 'data': 'xxxx'})

示例二: 基于ModelSerializer

# models.py
from django.db import models


class Role(models.Model):
    """ 角色表 """
    title = models.CharField(verbose_name='名称', max_length=32)


class Department(models.Model):
    """ 部门表 """
    title = models.CharField(verbose_name='名称', max_length=32)


class UserInfo(models.Model):
    username = models.CharField(verbose_name='用户名', max_length=32)
    age = models.CharField(verbose_name='年龄', max_length=32)
    level_choice = ((1, 'VIP'), (2, 'SVIP'), (3, 'PARTNER'))
    level = models.CharField(verbose_name='级别', choices=level_choice, max_length=32)
    email = models.CharField(verbose_name='邮箱', max_length=32)

    # 创建外键
    depart = models.ForeignKey(verbose_name="部门", to="Department", on_delete=models.CASCADE)

    # 多对多
    roles = models.ManyToManyField(verbose_name="角色", to="Role")

# views.py
# 基于ModelSerializer
class UserModelSerializer(serializers.ModelSerializer):
    email1 = serializers.CharField(label='邮箱1', validators=[EmailValidator, ])

    class Meta:
        model = models.UserInfo
        fields = ['username', 'age', 'email', 'email1', 'roles']  # 需要传入的数据, 多对多
        extra_kwargs = {
            'username': {'min_length': 4, 'max_length': 32},
            'age': {'max_length': 3}
        }
       	
        def valicate_email(self, value):
            ....
            return value


class UserView(APIView):
    """ 用户管理 """

    def post(self, request):
        """ 添加用户 """
        ser = UserModelSerializer(data=request.data)  # 将请求体数据传入, 这个request.data可以解析各种数据
        if not ser.is_valid():
            return Response({'code': 1006, 'data': ser.errors})
        print(ser.validated_data)

        # 将数据保存到数据库
        ser.validated_data.pop('email1')  # 删除不需要存入数据库的数据
        ser.save(level=1, depart_id=1)  # 加入初始化数据
        return Response({'code': 0, 'data': '创建成功'})

2. 序列化

示例一: 序列化基本字段

class UserModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = ['username', 'age', 'level', 'email', 'depart', 'roles']  # 序列化基本字段


class UserView(APIView):
    """ 用户管理 """
    def get(self, request):
        """ 序列化数据 """
        queryset = models.UserInfo.objects.all()
        ser = UserModelSerializer(instance=queryset, many=True)
        print(ser.data)
        return Response({'code': 0, 'data': ser.data})

返回值:

HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "code": 0,
    "data": [
        {
            "username": "ifeng",
            "age": "11",
            "level": 1,
            "email": "ifeng190410@gmail.com",
            "depart": 1,
            "roles": []
        },
        {
            "username": "Mcoco",
            "age": "11",
            "level": 1,
            "email": "ifeng190410@gmail.com",
            "depart": 1,
            "roles": [
                1,
                2
            ]
        }
    ]
}

示例二: 自定义字段

from django.forms.models import model_to_dict
from rest_framework import serializers
from rest_framework.response import Response
from rest_framework.views import APIView

from api import models

class UserModelSerializer(serializers.ModelSerializer):
    # 自定义字段
    level_text = serializers.CharField(source="get_level_display")
    depart = serializers.CharField(source='depart.title')

    roles = serializers.SerializerMethodField()
    extra = serializers.SerializerMethodField()

    class Meta:
        model = models.UserInfo
        fields = ['username', 'age', 'level_text', 'email', 'depart', 'roles', 'extra']

    def get_roles(self, obj):
        data_list = obj.roles.all()
        return [model_to_dict(item, ['id', 'title']) for item in data_list]

    def get_extra(self, obj):
        return 666


class UserView(APIView):
    """ 用户管理 """

    def get(self, request):
        """ 序列化数据 """
        queryset = models.UserInfo.objects.all()
        ser = UserModelSerializer(instance=queryset, many=True)
        print(ser.data)
        return Response({'code': 0, 'data': ser.data})

返回值:

{
   "code": 0,
   "data": [
       {
           "username": "ifeng",
           "age": "11",
           "level_text": "SVIP",
           "email": "ifeng190410@gmail.com",
           "depart": "后端",
           "roles": [],
           "extra": 666
       },
       {
           "username": "Mcoco",
           "age": "11",
           "level_text": "VIP",
           "email": "ifeng190410@gmail.com",
           "depart": "销售",
           "roles": [
               {
                   "id": 1,
                   "title": "CEO"
               },
               {
                   "id": 2,
                   "title": "CFO"
               }
           ],
           "extra": 666
       }
   ]
}

示例三: 序列化类的嵌套

嵌套主要是面向外键和多对多表的时候

3. 数据校验&序列化

注意点:

​ 我们在做多对多数据校验的时候, 后面如果需要新增数据, 则需要重写create方法, 如果需要更新数据, 则需要重写update方法

# mdoels.py
from django.db import models


# Create your models here.
class Role(models.Model):
    """ 角色表 """
    title = models.CharField(verbose_name='名称', max_length=32)


class Department(models.Model):
    """ 部门表 """
    title = models.CharField(verbose_name='名称', max_length=32)


class UserInfo(models.Model):
    username = models.CharField(verbose_name='用户名', max_length=32)
    age = models.CharField(verbose_name='年龄', max_length=32)
    level_choice = ((1, 'VIP'), (2, 'SVIP'), (3, 'PARTNER'))
    level = models.SmallIntegerField(verbose_name='级别', choices=level_choice)  # 类型为Int
    email = models.CharField(verbose_name='邮箱', max_length=32)

    # 创建外键
    depart = models.ForeignKey(verbose_name="部门", to="Department", on_delete=models.CASCADE)

    # 多对多
    roles = models.ManyToManyField(verbose_name="角色", to="Role")
# views.py
# 数据校验&序列化
class DepartModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Department
        fields = ['id', "title"]
        extra_kwargs = {
            "id": {"read_only": False},  # 数据验证, 需传入id, 为后续的create做准备
            "title": {"read_only": True}  # 序列化
        }


class RoleModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Role
        fields = ['id', "title"]
        extra_kwargs = {
            "id": {"read_only": False},  # 数据校验, 需传入id, 为后续的create做准备
            "title": {"read_only": True}  # 序列化
        }


class UserModelSerializer(serializers.ModelSerializer):
    level_text = serializers.CharField(source="get_level_display", read_only=True)  # read_only -> 只序列化, 但是不数据校验

    # Serializer嵌套,如果不设置read_only,一定要自定义create和update,自定义新增和更新的逻辑。
    depart = DepartModelSerializer(many=False)
    roles = RoleModelSerializer(many=True)

    extra = serializers.SerializerMethodField(read_only=True)
    email2 = serializers.EmailField(write_only=True)  # write_only -> 只数据校验不序列化

    # 数据校验:username、email、email2、部门、角色信息
    class Meta:
        model = models.UserInfo
        # username, age, email是即read_only也write_only
        fields = [
            "username", "age", "email", "level_text", "depart", "roles", "extra", "email2"
        ]
        # 给字段添加额外参数
        extra_kwargs = {
            "age": {"read_only": True},
            "email": {"validators": [EmailValidator, ]},
        }

    def get_extra(self, obj):
        return 666

    def validate_username(self, value):  # 钩子方法
        return value

    # 新增加数据时, 因为无法解决m2m的储存问题. 所以需要重写create方法
    def create(self, validated_data):
        """
        	如果有嵌套的Serializer,在进行数据校验时,只有两种选择:
              	1. 将嵌套的序列化设置成 read_only
              	2. 自定义create和update方法,自定义新建和更新的逻辑
            注意:用户端提交数据的格式。
        """
        """
        validated_data:
        	OrderedDict([('username', 'xiaoergu'), ('email', 'xiaoergu@gmail.com'), ('depart', OrderedDict([('id', 2)])), ('roles', [OrderedDict([('id', 1)]), OrderedDict([('id', 2)])]), ('email2', 'budianlong@gmail.com')])
        """
        depart_id = validated_data.pop('depart')['id']  # 拿到depart的id

        role_id_list = [ele['id'] for ele in validated_data.pop('roles')]  # 拿到roles的所有id

        # 新增用户表
        validated_data['depart_id'] = depart_id
        user_object = models.UserInfo.objects.create(**validated_data)

        # 在用户表和角色表的关联表中添加对应关系, django-orm知识
        user_object.roles.add(*role_id_list)

        return user_object


class UserView(APIView):
    """ 用户管理 """

    def get(self, request):
        """ 添加用户 """
        queryset = models.UserInfo.objects.all()
        ser = UserModelSerializer(instance=queryset, many=True)
        return Response({"code": 0, 'data': ser.data})

    def post(self, request):
        """ 添加用户 """
        ser = UserModelSerializer(data=request.data)
        if not ser.is_valid():
            return Response({'code': 1006, 'data': ser.errors})

        print(ser.validated_data)
        ser.validated_data.pop('email2')

        instance = ser.save(age=18, level=3)

        # 新增之后的一个对象(内部调用UserModelSerializer进行序列化)
        print(instance)
        # ser = UserModelSerializer(instance=instance, many=False)
        # ser.data

        return Response({'code': 0, 'data': ser.data})

返回值:

4. 源码分析

底层源码实现:

序列化的底层源码实现有别于上述其他的组件,序列化器相关类的定义和执行都是在视图中被调用的,所以源码的分析过程可以分为:定义类、序列化、数据校验。

源码1:序列化过程

源码2:数据校验过程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/867513.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

商品执行标准和卫生标准

纸巾 纸巾纸(抽纸) 作用&#xff1a;柔韧细腻、不易掉毛掉粉&#xff0c;擦汗时不易破碎、残留&#xff0c;常用于面部的清洁。 执行标准GB/T20808 卫生标准GB15979 主要成份&#xff1a;原生木浆 质量等级&#xff1a;优等品 细菌菌落总数<200cfu/g 卫生纸(厕纸) 作用&…

Scratch 之 枪战的枪械画法

大家可以参考百度图片寻找到的AK-47图片&#xff1a;AK47图片 此处我以MK18作为参照&#xff0c;MK18的造型可以在资源中获取 资源链接&#xff1a;https://download.csdn.net/download/leyang0910/88136393 对于不必要的&#xff08;繁琐的&#xff09;线条&#xff0c;我们可…

配置vscode

配置vscode 设置相关 网址&#xff1a;https://code.visualstudio.com/ 搜索不要用百度用这个&#xff1a;cn.bing.com 1.安装中文包 Chinese (Simplified) (简体中文) 2.安装 open in browser 3.安装主题 Atom One Dark Theme 4. 安装图标样式 VSCode Great Icons 5.安装 L…

【力扣每日一题】23. 合并 K 个升序链表 暴力法-快排 8.12打卡

文章目录 题目思路代码 题目 合并 K 个升序链表 难度&#xff1a; 困难 描述&#xff1a; 给你一个链表数组&#xff0c;每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表。 示例 1&#xff1a; 输入&#xff1a;lists [[1,4,5…

Qt6之QStackedWidget——Qt仿ToDesk(2)

一、 QStackedWidget概述 QStackedWidget也叫堆栈窗体类&#xff0c;它继承于QFrame&#xff0c;主要与QListWidget等结合使用&#xff0c;实现“一个界面多个页面切换”。 二、QStackedWidget示例 如下图&#xff0c;当点击左边 QListWidget里的菜单时&#xff0c;右边跟随切…

深入浅出流批一体理论篇——数据架构的演进

一、前大数据时代 人人都知道罗马不是一天建成的&#xff0c;但没人告诉过你罗马是怎样一天天建成的。你看见罗马时&#xff0c;它就已经是罗马了。当我进阿里时&#xff0c;正是这样的感觉。我没有经历过阿里数据架构&#xff08;包括平台工具&#xff09;从0到1的过程。我相…

计算机科学的伟大变革:从机械计算到人工智能

摘要 计算机科学作为一门学科&#xff0c;经历了几十年的发展和演变。本论文旨在探讨计算机科学领域的伟大变革&#xff0c;从最早的机械计算设备到如今的人工智能系统。通过回顾历史、分析技术进步以及展望未来&#xff0c;我们可以清晰地看到计算机科学如何塑造了现代社会&a…

Flink学习记录

可以快速搭建一个Flink编写程序 mvn archetype:generate \-DarchetypeGroupIdorg.apache.flink \-DarchetypeArtifactIdflink-quickstart-java \-DarchetypeVersion1.17.1 \-DgroupIdcom.zxx.langhuan \-DartifactIdlanghuan-flink \-Dversion1.0.0-SNAPSHOT \-Dpackagecom.zx…

SpringBoot复习:(33)WebMvcAutoconfiguration内部静态类WebMvcAutoConfigurationAdapter

WebMvcAutoconfiguration内部静态类WebMvcAutoConfigurationAdapter实现了WebMvcConfigurer接口&#xff0c;重写了一些方法&#xff0c;也就是默认对Spring Mvc进行了一些配置: 该静态类上有个**Import**注解&#xff1a; Import(EnableWebMvcConfiguration.class) 它的父类…

前端笔试题1

HTML/CSS 题1&#xff1a; 1&#xff0e;使用CSS 让该节点不可见&#xff0c;方法越多越好。 <div class"hidden">Hi</div> 使用CSS 让节点不可见的方法有以下几种&#xff1a; 把 visibility 属性设置为 hidden&#xff0c;这样元素框不会被绘制&…

伯俊ERP对接打通金蝶云星空表头表体组合查询接口与采购订单新增接口

伯俊ERP对接打通金蝶云星空表头表体组合查询接口与采购订单新增接口 数据源平台:伯俊ERP 伯俊科技&#xff0c;依托在企业信息化建设方面的领先技术与实践积累&#xff0c;致力于帮助企业实现全渠道一盘货。伯俊提供数字经营的咨询与系统实施&#xff0c;助力企业信息化升级、加…

【C++】STL初识

1.STL的基本概念 2.vector存放内置数据类型 #include <iostream> using namespace std; #include <vector> #include <algorithm>void MyPrint(int val) {cout << val << endl; }void test01() {//创建vector容器对象&#xff0c;并且通过模板参…

DP1.4接口的PCB布局布线要求

DP接口即为DisplayPort接口&#xff0c;是由视频电子标准协会发布的显示接口。DP接口将在传输视频信号的同时加入对高清音频信号传输的支持&#xff0c;并且同时支持更高的分辨率以及刷新率。DP1.4通信端口规范新标准基于DP1.3规范&#xff0c;宽度不变但加入了显示压缩流技术&…

Spring-Cloud-Loadblancer详细分析_3

前两篇文章介绍了加载过程&#xff0c;本文从Feign的入口开始分析执行过程&#xff0c;还是从FeignBlockingLoadBalancerClient.execute来入手 public class FeignBlockingLoadBalancerClient implements Client {private static final Log LOG LogFactory.getLog(FeignBlock…

【TypeScript】进阶之路语法细节,类型和函数

进阶之路 类型别名(type)的使用接口(interface)的声明的使用二者区别&#xff1a; 联合类型和交叉类型联合类型交叉类型 类型断言获取DOM元素 非空类型断言字面量类型的使用类型缩小&#xff08;类型收窄&#xff09;TypeScript 函数类型函数类型表达式内部规则检测函数的调用签…

什么是响应式设计?列举几种实现响应式设计的方法。

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是响应式设计&#xff1f;⭐ 实现响应式设计的方法⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏…

Linux:Shell编程之免交互

目录 绪论 1、here Document免交互 1.1 格式 1.2 cat结合免交互实现重定向输出到指定文件 1.3 变量替换 2、Expect免交互 2.1 三种写法 3、免交互实现普通用户切换root 3.1 send_user 4、接收参数 5、嵌入执行模式 6、ssh远程登录 绪论 免交互&#xff1a;不需要人…

【Linux进行时】进程概念

进程的概念 什么是进程呢&#xff1f; ❓首先我们需要认识一下什么叫进程呢&#xff1f; 课本概念&#xff1a;程序的一个执行实例&#xff0c;正在执行的程序等 &#x1f525;内核观点&#xff1a;担当分配系统资源&#xff08;CPU时间&#xff0c;内存&#xff09;的实体。…

海外ASO优化之关于应用的营销2

在目标受众中建立信任度&#xff0c;并获得博客/新闻网站的热榜&#xff0c;这样自然会提高应用的知名度和目标受众的认知度。就博客读者而言&#xff0c;需要找出推荐的最佳时间和真正推动我们应用是什么。 1、提供了App Store或Google Play的直接链接。 我们首先需要创建一个…