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

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')@propertydef publish_detail(self):return {'name': self.publish.name, 'email': self.publish.email}@propertydef author_list(self):author_list = []for i in self.author.all():author_list.append({'name': i.name, 'age': i.age})return author_listclass 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和instanceserializer = 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 Authorclass AuthorSerializer(serializers.ModelSerializer):class Meta:model = Authorfields = '__all__'extra_kwargs = {'name': {'max_length': 8},  # 限制name不能超过8'author_detail': {'read_only': True},}
  • 此时序列化类中就不需要一个个写字段了,ModelSerializer会自动跟表做对应关系
  • model:需要对应的表
  • fields:用于指定需要被序列化的字段,'__all__'为全部['name', 'age']为指定
  • extra_kwargs:类似钩子函数,将对应字段加上限制

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

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

相关文章

Linux【实战篇】—— NFS服务搭建与配置

目录 一、介绍 1.1什么是NFS? 1.2客户端与服务端之间的NFS如何进行数据传输? 1.3RPC和NFS的启动顺序 1.4NFS服务 系统守护进程 二、安装NFS服务端 2.1安装NFS服务 2.2 创建共享目录 2.3创建共享目录首页文件 2.4关闭防火墙 2.5启动NFS服务 2.…

秋叶Stable diffusion的创世工具安装-带安装包链接

来自B站up秋葉aaaki,近期发布了Stable Diffusion整合包v4.7版本,一键在本地部署Stable Diffusion!! 适用于零基础想要使用AI绘画的小伙伴~本整合包支持SDXL,预装多种必须模型。无需安装git、python、cuda等任何内容&am…

day9 | 栈与队列 part-1 (Go) | 232 用栈实现队列、225 用队列实现栈

今日任务 栈与队列的理论基础 (介绍:代码随想录)232 用栈实现队列(题目: . - 力扣(LeetCode))225 用队列实现栈 (题目: . - 力扣(LeetCode) ) 栈与队列的理论基础 栈 : 先进后出 队列: 后进先出 老师给的讲解:代码随想录 …

记一次centos合并excel,word,png,pdf为一个整体pdf的入坑爬坑过程(一直显示宋体问题)。

一、背景 原先已经简单实现了excel,word,png,pdf合成一个整体pdf的过程。并将它弄到docker容器中。 1、原先入坑的技术栈 php:7.4 (业务有涉及)php第三方包 setasign\Fpdi\Fpdi : 2.3.6 (pdf合并)libreoffice : 5.3.6.1ImageMagick: 6.9.10-68 2、…

本地PC安装eNSP Pro完成简单的WLAN实验

前言 上个月底华为更新一版eNSP Pro,新增了AC、AP、STA等设备,也就是说可以在eNSP中进行WLAN相关的实验了。之前写过一篇文章《将eNSP Pro部署在华为云是什么体验》介绍了怎么在华为云上部署eNSP Pro,这次使用本地PC机在虚拟机中安装eNSP Pr…

RF测试笔记:三阶交调失真概述及测试

1. 交调失真会带来哪些影响? 无线通信系统中,交调失真不仅会影响发射链路的性能,还会影响接收链路的性能。 对于发射链路,非线性最严重的部件非功率放大器莫属,当信号为宽带调制信号时,无论是在信号带宽内…

13 Php学习:面向对象

PHP 面向对象 面向对象(Object-Oriented,简称 OO)是一种编程思想和方法,它将程序中的数据和操作数据的方法封装在一起,形成"对象",并通过对象之间的交互和消息传递来完成程序的功能。面向对象编…

基于Python的深度学习的中文情感分析系统(V2.0),附源码

博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…

【STL详解 —— list的介绍及使用】

STL详解 —— list的介绍及使用 list的介绍list的介绍使用list的构造list iterator的使用list capacitylist element accesslist modifiers 示例list的迭代器失效 list的介绍 list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭…

基于Docker构建CI/CD工具链(八)用nginx收集测试报告

当前,我们已经介绍了如何使用 Apifox 和 JMeter 进行测试,尽管控制台已经输出了测试结果,但在实际工作中,我们通常需要更详细的测试报告。 测试报告在测试过程中已经生成,只需将其托管起来以便查阅。如果你有现成的 C…

C++11 设计模式4. 抽象工厂(Abstract Factory)模式

问题的提出 从前面我们已经使用了工厂方法模式 解决了一些问题。 现在 策划又提出了新的需求:对于各个怪物,在不同的场景下,怪物的面板数值会发生变化, //怪物分类:亡灵类,元素类,机械类 …

MATLAB 自定义实现点云法向量和曲率计算(详细解读)(64)

MATLAB 自定义实现点云法向量和曲率计算(详细解读)(64) 一、算法介绍二、算法步骤三、算法实现1.代码 (完整,注释清晰,可直接用)2.结果一、算法介绍 首先说明: ------这里代码手动实现,不调用matlab提供的法向量计算接口,更有助于大家了解法向量和曲率的计算方法,…

docker部署Prometheus+AlertManager实现邮件告警

文章目录 一、环境准备1、硬件准备(虚拟机)2、关闭防火墙,selinux3、所有主机安装docker 二、配置Prometheus1、docker启动Prometheus 三、添加监控节点1、docker启动node-exporter 四、Prometheus配置node-exporter1、修改prometheus.yml配置…

【网站项目】摄影竞赛小程序

🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。🌹赠送计算机毕业设计600个选题excel文件,帮助大学选题。赠送开题报告模板&#xff…

刷题之动态规划-回文串

前言 大家好,我是jiantaoyab,开始刷动态规划的回文串类型相关的题目 动态规划5个步骤 状态表示 :dp数组中每一个下标对应值的含义是什么>dp[i]表示什么状态转移方程: dp[i] 等于什么1 和 2 是动态规划的核心步骤,…

某次众测的加解密对抗

前言 起源于某次众测中,遇到请求包响应包全密文的情况,最终实现burp中加解密。 用到的工具有 sekiro(rpc转发)flask(autodecoder自定义接口)autodecoder(burp插件转发) debug部分…

ClickHouse--18--argMin() 和argMax()函数

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 argMin() 和argMax()函数业务场景使用案例1.准备表和数据:业务场景一:查看salary 最高和最小的user业务场景二:根据更新时间获取…

【fastapi】搭建第一个fastapi后端项目

本篇文章介绍一下fastapi后端项目的搭建。其实没有什么好说的,按照官方教程来即可:https://fastapi.tiangolo.com/zh/ 安装依赖 这也是我觉得python项目的槽点之一。所有依赖都安装在本地,一旦在别人电脑上编写项目就又要安装一遍。很扯淡。…

最优算法100例之45-不用循环乘法求1-n的和

专栏主页:计算机专业基础知识总结(适用于期末复习考研刷题求职面试)系列文章https://blog.csdn.net/seeker1994/category_12585732.html 题目描述 要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句。不能用循…

【opencv】示例-neural_network.cpp 使用机器学习模块创建并训练一个简单的多层感知机(神经网络)模型...

#include <opencv2/ml/ml.hpp> // 引入OpenCV的机器学习模块using namespace std; // 使用标准命名空间 using namespace cv; // 使用OpenCV命名空间 using namespace cv::ml; // 使用OpenCV机器学习命名空间int main() {//创建随机训练数据Mat_<float> data(100, …