DRF多表关联的序列化和反序列化

news2025/6/7 21:49:06

DRF多表关联的序列化和反序列化

目录

  • DRF多表关联的序列化和反序列化
    • 序列化定制字段source
      • 一对多的序列化
    • 多表关联的序列化
      • 方式1:在表模型中定义方法
      • 方式2:定制返回格式SerializerMethodField
      • 方式3:子序列化
    • 多表关联的反序列化
      • 反序列化保存一对多关联字段
        • create
        • update
      • 反序列化保存多对多关联字段
        • create
      • 反序列化保存一对一关联字段
        • create
        • update
    • ModelSerializer类下的序列化和反序列化

序列化定制字段source

# models.py
class Book(models.Model):
    title = models.CharField(max_length=32, null=True)
    price = models.IntegerField(null=True)
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, null=True)
    author = models.ManyToManyField(to='Author', null=True)
# views.py
class book(APIView):
    def get(self, request):
        book_obj = models.Book.objects.all()
        serializer = BookSerializer(instance=book_obj, many=True)
# serializers.py
class BookSerializer(serializers.Serializer):
	book_name = serializers.CharField(source='title')

这是一个serializers的字段,当我要进行序列化时

  • book_name决定了该字段在前端显示的名字
  • source = 'title'决定了返回给前端的内容是表中的book表中的title字段属性
  • 因此只要source指定了属性值,book_name可以随便改名

一对多的序列化

表中的publish字段

# models.py
class Book(models.Model):
	publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    
class Publish(models.Model):
    name = models.CharField(max_length=32, null=True)
    email = models.EmailField(null=True)

serializers中的序列化

# serializer.py
publish_name = serializers.CharField(source='publish.name')
publish_email = serializers.EmailField(source='publish.email')
  • 当publish是个对象时直接序列化只能打印它的字典对象
  • 因此需要在source中写跨表查询
  • 注意:所有类型都可以用CharField响应

响应结果:

image-20240412215612520

多表关联的序列化

方式1:在表模型中定义方法

# models.py
class Book(models.Model):
    title = models.CharField()
    price = models.IntegerField()
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    author = models.ManyToManyField(to='Author')

    @property
    def publish_detail(self):
        return {'name': self.publish.name, 'email': self.publish.email}
    
    @property
    def author_list(self):
        author_list = []
        for i in self.author.all():
            author_list.append({'name': i.name, 'age': i.age})
        return author_list

class Publish(models.Model):
    name = models.CharField(max_length=32,)
    email = models.EmailField()
# serializers.py
class BookSerializer(serializers.Serializer):
    book_name = serializers.CharField(source='title')
    price = serializers.IntegerField()
    publish_detail = serializers.DictField()
    author_list = serializers.ListField()
  • publish_detail = serializers.DictField()相当于调用了book表中的publish_detail方法,返回的是一个对象

image-20240412223016991

方式2:定制返回格式SerializerMethodField

# models.py
class Book(models.Model):
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, null=True)
    author = models.ManyToManyField(to='Author', null=True)
# serializers.py
class BookSerializer(serializers.Serializer):
    publish_dict = serializers.SerializerMethodField()

    def get_publish_dict(self, obj):
        return {'name': obj.publish.name, 'email': obj.publish.email}

    author_list = serializers.SerializerMethodField()

    def get_author_list(self, obj):
        author_list = []
        for i in obj.author.all():
            author_list.append({'name': i.name, 'age': i.age})
        return author_list
  • SerializerMethodField():允许你在序列化器中定义一个基于方法的字段
  • 方法名必须是get_ +需要定制的序列化字段
  • obj:相当于将后端定义的表对象传了过来,你可以在里面做跨表查询

方式3:子序列化

# models.py
class Book(models.Model):
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    author = models.ManyToManyField(to='Author')
# serializers.py
class PublishSerializer(serializers.Serializer):
    name = serializers.CharField()
    email = serializers.EmailField()
    
class AuthorSerializer(serializers.Serializer):
    name = serializers.CharField()
    age = serializers.IntegerField()

class BookSerializer(serializers.Serializer):
    publish = PublishSerializer()
    author_detail = AuthorSerializer(source='author', many=True)
  • 直接在serializers中新建PublishSerializer类,让publish字段调用这个类就能获取他的所有属性
  • publishauthor_detail可以任意改名,如果改名的话需要source参数对应表中的字段名,否则会报错
  • 因为author字段是一个列表,所以要在参数中加上many=True

多表关联的反序列化

反序列化保存一对多关联字段

create
# models.py
class Book(models.Model):
    title = models.CharField(max_length=32, null=True)
    price = models.IntegerField(null=True)
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, null=True)

publish字段一对多关系绑定了Publish表,因此在Book表中显示的是publish_id

image-20240413162012394

在反序列化校验Book对象时,django并不认识Publish表,因此只会识别publish_id,那么前端发送请求时也要传入publish_id

# serializers.py
class BookSerializer(serializers.Serializer):
    book_name = serializers.CharField(source='title')
    price = serializers.IntegerField()
    publish_detail = PublishSerializer(source='publish', read_only=True)

    publish_id = serializers.IntegerField(write_only=True)

    def create(self, validated_data):
        print(validated_data)
        publish_id = validated_data.pop('publish_id')
        book_obj = models.Book.objects.create(**validated_data, publish_id=publish_id)
        return book_obj

在反序列化校验publish_id时为了不影响原先的序列化校验,要在不需要反序列化校验的参数后面加入read_only=True参数,同时publish_id加入write_only参数只作用于反序列化校验

  • read_only=True:该字段只会用于序列化校验
  • write_only=True:该字段只会用于反序列化校验

重写create方法(重点

  • 此时前端传入的参数应为:
    image-20240413163621635

  • print(validated_data)打印结果为:

    {'title': '三体', 'price': 900, 'publish_id': 2}
    
  • 先不看代码来试一下,如果这是我直接book_obj = models.Book.objects.create(**validated_data)会发生什么?

book_obj = models.Book.objects.create(**validated_data)
print(book_obj.publish_id)
# 结果:None
  • 这是因为,虽然validated_data包含了publish_id字段,但是此时它并不具备外键属性,Django仅将他作为一个普通字段进行反序列化

  • 那么理所当然的后端的序列化结果也不会成功,因为publish_detail是根据外键属性取值的

  • 所以此时这里应该明确用publish_id作为publish_id字段的参数

# 先将publish_id参数从vaildated_data中踢出,让前两个参数以**方式传入后再指定publish_id
publish_id = validated_data.pop('publish_id')

# 左边的publish_id是字段,右边的publish_id是前端传来的数据
book_obj = models.Book.objects.create(**validated_data, publish_id=publish_id)

# 这种方法和上面的含义相同,用其中一种即可
book_obj = models.Book.objects.create(title=validated_data.get('title'), price=validated_data.get('price'), publish_id=publish_id)

image-20240413165250525

  • 传入成功应正确返回序列化后的字段
update
# views.py
def put(self, request, u_id):
    book_obj = models.Book.objects.filter(pk=u_id).first()
    # 改对象必须传data和instance
    serializer = BookSerializer(instance=book_obj, data=request.data)
    if serializer.is_valid():
        serializer.save()
        return Response({'code': '200', 'msg': "修改成功", 'result': serializer.data})
    else:
        return Response({'code': '201', 'msg': serializer.errors})
# serializers.py
def update(self, instance, validated_data):
    instance.publish_id = validated_data.get('publish_id')
	instance.title = validated_data.get('title')
    instance.price = validated_data.get('price')
    instance.save()

    return instance
  • updata相比于create就多了个instance参数
  • instance就是views层传过来的模型对象,将要修改的字段保存为validated_data中的数据即可

反序列化保存多对多关联字段

create

首先定义一个author字段,与Author表绑定多对多关系字段

class Book(models.Model):
    title = models.CharField(max_length=32, null=True)
    price = models.IntegerField(null=True)
    author = models.ManyToManyField(to='Author', null=True)

class Author(models.Model):
    name = models.CharField(max_length=10, null=True)
    age = models.SmallIntegerField(null=True)

那么此时的数据便不会存在于BookAuthor中,而是自动新建一个book_author表,例如:

image-20240413183246545

def create(self, validated_data):
    author_id = validated_data.pop('author_id')
    book_obj = models.Book.objects.create(**validated_data)
    book_obj.author.add(author_id)
    return book_obj
  • 首先将author_id踢出,然后将其单独插入中间表book_author,具体原因跟一对多关系相同

反序列化保存一对一关联字段

create
# models.py
class Author(models.Model):
    name = models.CharField(max_length=10, null=True)
    age = models.SmallIntegerField(null=True)
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE, null=True)

    class Meta:
        db_table = 'author'

    def author_dict(self):
        return {'爱好': self.author_detail.hobby, '地址': self.author_detail.addr}


class AuthorDetail(models.Model):
    hobby = models.CharField(max_length=32, null=True)
    addr = models.CharField(max_length=50, null=True)
# serializers.py
class AuthorSerializer(serializers.Serializer):
    # 序列化和反序列化字段
    name = serializers.CharField()
    age = serializers.IntegerField()
	# 序列化字段(在模型中定义方法)
    author_dict = serializers.DictField()

    # 重写create方法
    def create(self, validated_data):
        print(validated_data)
        author_dict = validated_data.pop('author_dict')
        author_detail = AuthorDetail.objects.create(**author_dict)

        author_obj = Author.objects.create(**validated_data, author_detail=author_detail)

        return author_obj
  • 这里需要将author_dict里需要的字段全部在前端传入,例:
{
        "name": "张三",
        "age": 18,
        "author_dict":{
            "hobby":"run",
            "addr":"北京"
        }
}
update

前面的都不需要动,只需重写update方法

def update(self, instance, validated_data):
    # 与create相同,要在中间表更新数据
    author_dict = validated_data.pop('author_dict')
    AuthorDetail.objects.filter(pk=instance.author_detail.pk).update(**author_dict)
    
    # 其他字段不变,detail_id字段不上传,因为是一对一绑定关系所以没必要
    instance.name = validated_data.get('name')
    instance.age = validated_data.get('age')
    instance.save()

    return instance
  • instance.author_detail.pkinstance对象获取到中间表author_detail的pk

ModelSerializer类下的序列化和反序列化

# models.py
class Author(models.Model):
    name = models.CharField(max_length=10, null=True)
    age = models.SmallIntegerField(null=True)
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE, null=True)
# views.py
class author(APIView):
    def get(self, request):
        author_obj = models.Author.objects.all()
        serializer = AuthorSerializer(instance=author_obj, many=True)
        return Response({'code': '200', 'msg': '查询成功', 'result': serializer.data})
# serializer.py
from app.models import Author

class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = '__all__'
        extra_kwargs = {
            'name': {'max_length': 8},  # 限制name不能超过8
            'author_detail': {'read_only': True},
        }

  • 此时序列化类中就不需要一个个写字段了,ModelSerializer会自动跟表做对应关系
  • model:需要对应的表
  • fields:用于指定需要被序列化的字段,'__all__'为全部['name', 'age']为指定
  • extra_kwargs:类似钩子函数,将对应字段加上限制

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

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

相关文章

Ubuntu 上配置静态IP地址

Ubuntu 上配置静态IP地址编辑网络配置文件配置静态IP地址应用配置更改 Ubuntu 上配置静态IP地址 在终端中,运行以下命令来编辑网络配置文件 编辑网络配置文件 sudo cat /etc/netplan/01-netcfg.yaml在编辑器中,找到用于您的网络接口的配置部分。例如…

使用阿里云试用Elasticsearch学习:创建仪表板pivot、搜索discover和仪表板dashboard

文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/transform-examples.html#example-clientips 在kibana左栏打开Transforms,并创建Transforms(转换) Management > Stack Management > Data > T…

【Qt】界面优化

目录 一、QSS 1.1 基本语法 1.2 QSS设置方法 1.2.1 指定控件样式设置 1.2.2 全局样式设置 1.2.3 从文件加载样式表 1.2.4 使用Qt Designer编辑样式 1.3 选择器 1.3.1 介绍 1.3.2 子控件选择器 1.3.3 伪类选择器 1.4 样式属性(盒模型) 1.5 代码示例(登录界面) 二、…

自动化测试之httprunner框架hook函数实操

本篇介绍httprunner中hook函数的使用,以及通过编程能力实现建设自动化测试更全面的场景覆盖 前置: 互联网时代让我们更快的学习到什么是Httprunner 正文: 经过上文了解到这个框架怎么使用之后,我们开始来探讨一下我们为什么要用…

SecureCRT通过USB-Servial ch340串口无法连接单片机

通过USB To TTL连线 STM32F103-PRO,烧制程序到单片机上,通过SecureCRT通过USB-Servial ch340串口无法链接RS232升USB转TTL连接正确 开发板连接正确 问题:SecureCRT串口连接没有反应 问题分析:1、检查ch340串口驱动 查看设备管…

海洋信息管理系统:守护蓝色星球,促进海洋经济新发展

海洋,覆盖地球表面超过七成的广阔水域,是生命之源,也是经济发展的重要空间。然而,随着人类活动的增加,海洋生态环境面临严峻挑战,海洋资源的可持续利用成为全球关注的焦点。在这样的背景下,构建…

openssl 如何从pfx格式证书 获取证书序列号信息

已知:一个个人证书文件 test.pfx 求:如何通过openssl查看其对应证书的序列号信息? 踩坑之:unable to load certificate! openssl x509 -in xxx.cert -noout -serial 命令可查看证书序列号,但是这个-in 的输入必须是私…

一步一步学习使用 MediaSource 实现动态媒体流

学习前的参考 为什么视频网站的视频链接地址是blob? - 掘金 MediaSource - Web API 接口参考 | MDN 在示例中前往下载源代码: netfix/demo/bufferWhenNeeded.html at gh-pages nickdesaulniers/netfix GitHub 下载 demo 目录,对 bufferW…

量子信息产业生态研究(一):关于《量子技术公司营销指南(2023)》的讨论

写在前面。量子行业媒体量子内参(Quantum Insider)编制的《量子技术公司营销指南》是一本实用的英文手册,它旨在帮助量子科技公司建立有效的营销策略,同时了解如何将自己定位成各自的行业专家。本文对这篇指南的主要内容进行了翻译…

C语言中的编译和链接

系列文章目录 文章目录 ​编辑 系列文章目录 文章目录 前言 一、 翻译环境和运行环境 二、 翻译环境 2.1 编译 2.1.1 预处理 2.1.2 编译 2.1.2.1 词法分析 : 2.1.2.2 语法分析 2.1.2.3 语义分析 2.1.3 汇编 2.2 链接 三、运行环境 前言 在我们平常的写代码时,我们很…

set 类 和 map 类

1. 关联式容器 关联式容器也是用来存储数据的&#xff0c;与序列式容器不同的是&#xff0c;其里面存储的是<key, value>结构的 键值对&#xff0c;在数据检索时比序列式容器效率更高 2. 键值对 用来表示具有一一对应关系的一种结构&#xff0c;该结构中一般只包含…

你的系统是如何跟MySQL打交道的

1、Java 工程师眼中的数据库是什么东西? 从今天开始&#xff0c;我们将要开始一个MySQL的专栏&#xff0c;一起来研究MySQL数据库的底层原理和各种实践案例&#xff0c;以及互联网公司的技术方案。 现在我们先来看看&#xff0c;在一个Java工程师眼中的数据库是什么东西? 平时…

开源 Ruo-Yi 项目引入 Mybatis-Plus:3.5.3 报错ClassNotFoundException:

开源 Ruo-Yi 项目引入 Mybatis-Plus:3.5.3 报错ClassNotFoundException&#xff1a; Caused by: java.lang.ClassNotFoundException: com.baomidou.mybatisplus.extension.plugins.MybatisPlusInter1 分析问题 控制台报错说明我们引入的 mybatis-plus 的依赖里找不到com.baom…

怎么恢复删除的回收站数据?分享多种恢复方法

在日常使用电脑的过程中&#xff0c;回收站是我们经常打交道的一个功能。它帮助我们管理不再需要的文件&#xff0c;但有时候&#xff0c;我们可能会不小心删除了重要文件&#xff0c;或者误清空了回收站。那么&#xff0c;面对这种情况&#xff0c;我们该如何恢复删除的回收站…

小程序面试题之性能优化提高10道

1.你使用过哪些方法&#xff0c;来提高微信小程序的应用速度&#xff1f; 提高页面加载速度 用户行为预测 减少默认data的大小 组件化方案 控制包的大小 压缩代码&#xff0c;清理无用代码 采用分包策略 启用本地缓存 参考地址&#xff1a;https://blog.csdn.net/wu_xianqiang/…

Linux上的可执行文件在Windows上是不能运行的

一、概要 1、可执行文件的格式 Linux上的可执行文件是elf格式的 Windows上的可执行文件是exe格式的 Linux上的可执行文件在Windows上是不能运行的 2、程序的普通构建与静态构建 普通构建&#xff1a; 一个.c文件&#xff0c;用gcc命令编译成可执行文件(程序)&#xff0c…

数据结构排序篇上

排序的概念及其运用 排序的概念 排序 &#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性 &#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&…

分类预测 | Matlab实现基于迁移学习和GASF-CNN-Mutilhead-Attention格拉姆角场和卷积网络多头注意力机制多特征分类预测/故障识别

分类预测 | Matlab实现基于迁移学习和GASF-CNN-Mutilhead-Attention格拉姆角场和卷积网络多头注意力机制多特征分类预测/故障识别 目录 分类预测 | Matlab实现基于迁移学习和GASF-CNN-Mutilhead-Attention格拉姆角场和卷积网络多头注意力机制多特征分类预测/故障识别分类效果基…

动态规划原理及其在优化问题中的应用解析

动态规划原理及其在优化问题中的应用解析 一、最优子结构二、重叠子问题三、何时使用动态规划法四、伪代码示例五、C代码示例七、详细说明动态规划原理7.1、最优子结构7.2 重叠子问题7.3 动态规划的实现 八、结论 动态规划是一种解决优化问题的方法&#xff0c;它通过将原问题分…

神经网络--反向传播算法推导

神经网络–反向传播算法推导 文章目录 神经网络--反向传播算法推导概述神经网络模型反向传导算法 概述 以监督学习为例&#xff0c;假设我们有训练样本集 ( x ( i ) , y ( i ) ) (x^{(i)},y^{(i)}) (x(i),y(i))&#xff0c;那么神经网络算法能提供一种复杂且非线性的假设模型 …