DRF之序列化组件ModelSerializer--嵌套序列化(一对多,多对多)
数据库之间互相关联,一对多和多对多,继承ModelSerializer的序列化器内部代码需要定制
为了多对多关联,我们同时使用自动多对多建立了editor和book的关系,
半自动多对多建立了author和book的关系
# models.py文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | # models.py from django.db import models # Create your models here. # 创建表book,author,publish # publish和book是一对多的关系,外键在book表 # author和book是多对多的关系,外键在book中,手动创建了第三张表book2author表示二者对应关系 class Book(models.Model): title = models.CharField(max_length = 255 , verbose_name = "书名" ) price = models.IntegerField(verbose_name = "价格" ) # publish和book 一对多关系 外键设在多的book表内 publish = models.ForeignKey(to = "Publish" , on_delete = models.CASCADE) # 采用半自动方式建立author和book表之间的多对多关系 authors = models.ManyToManyField(to = "Author" , through = "Book2Author" , through_fields = ( 'book' , 'author' ) ) # 采用全自动方式建立editor和book表之间的多对多关系 editors = models.ManyToManyField(to = "Editor" ) class Meta: db_table = "bookstore_book" verbose_name = "书籍" verbose_name_plural = verbose_name def __str__( self ): return self .title # editor表,和book多对多 class Editor(models.Model): name = models.CharField(max_length = 255 ,verbose_name = "主编姓名" ) class Meta: db_table = "bookstore_editor" verbose_name = "主编" verbose_name_plural = verbose_name def __str__( self ): return self .name class Publish(models.Model): name = models.CharField(max_length = 255 , verbose_name = "出版社名称" ) addr = models.CharField(max_length = 255 , verbose_name = "出版社地址" ) class Meta: db_table = "bookstore_publish" verbose_name = "出版社" verbose_name_plural = verbose_name def __str__( self ): return self .name # author表,和book表多对多 class Author(models.Model): name = models.CharField(max_length = 255 , verbose_name = "作者姓名" ) class Meta: db_table = "bookstore_author" verbose_name = "作者" verbose_name_plural = verbose_name def __str__( self ): return self .name # 半自动创建一个book和author的多对多关系表 # 和前面自动创建的book和editor的多对多关系表形成对比 class Book2Author(models.Model): book = models.ForeignKey(to = "Book" , on_delete = models.CASCADE) author = models.ForeignKey(to = "Author" , on_delete = models.CASCADE) class Meta: db_table = "bookstore_book2author" verbose_name = "书籍作者关系表" verbose_name_plural = verbose_name |
为了测试序列化和反序列化在使用ModelSerializer组件时的方法,我们从序列化GET请求和反序列化POST请求来分析
# serializers.py文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | # serializers.py from rest_framework import serializers from bookstore import models class BookModelSerializer(serializers.ModelSerializer): class Meta: model = models.Book fields = "__all__" # depth = 1 class EditorModelSerializer(serializers.ModelSerializer): class Meta: model = models.Editor fields = "__all__" class PublishModelSerializer(serializers.ModelSerializer): class Meta: model = models.Publish fields = "__all__" class AuthorModelSerializer(serializers.ModelSerializer): class Meta: model = models.Author fields = "__all__" class Book2AuthorModelSerializer(serializers.ModelSerializer): class Meta: model = models.Book2Author fields = "__all__" |
# views.py文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # views.py from rest_framework.viewsets import ModelViewSet from bookstore import models from bookstore import serializers # Create your views here. class BookModelViewSet(ModelViewSet): queryset = models.Book.objects. all () serializer_class = serializers.BookModelSerializer class EditorModelViewSet(ModelViewSet): queryset = models.Editor.objects. all () serializer_class = serializers.EditorModelSerializer class PublishModelViewSet(ModelViewSet): queryset = models.Publish.objects. all () serializer_class = serializers.PublishModelSerializer class AuthorModelViewSet(ModelViewSet): queryset = models.Author.objects. all () serializer_class = serializers.AuthorModelSerializer class Book2AuthorModelViewSet(ModelViewSet): queryset = models.Book2Author.objects. all () serializer_class = serializers.Book2AuthorModelSerializer |
完成models.py , serializers.py , views.py三个文件,我们使用Postman来测试GET请求和POST请求
一 GET请求
![]() |
![]() |
# 左侧为book列表,为列表套字典,右侧为单本书的详细信息,为字典格式
# 自动创建的多对多editors和半自动创建的authors都能够正确显示
# 但是,结果中并没有显示外键字段的详细信息,比如我同事需要authors,editors的姓名,id等信息
# 修改序列化器中的序列化深度depth属性=1就可以显示
1 2 3 4 5 6 7 8 9 | # serializers.py class BookModelSerializer(serializers.ModelSerializer): class Meta: model = models.Book fields = "__all__" depth = 1 # 序列化深度depth属性能够将关联属性的表属性也显示序列化出来 # 引用序列化深度depth属性的字段序列化时会自动变为readonly,所以更新或创建时,不能使用该序列化器类 |
可以看出不管是自动化创建的多对多关系,还是半自动创建的多对多关系,以及一对多关系,此时都能够正确的在序列化信息显示.
但是,一旦使用了含有序列化深度depth属性的序列化器,那么引用改属性的字段就无法进行更新或者创建
如果不使用深度属性depth,有另外一种方法也可以得到多层序列化信息,如下
1 2 3 4 5 6 7 8 9 10 | # serializers.py class BookModelSerializer(serializers.ModelSerializer): publish = PublishModelSerializer() # 如果有多条,就many=True editors = EditorModelSerializer(many = True ) authors = AuthorModelSerializer(many = True ) class Meta: model = models.Book fields = "__all__" # depth = 1 |
二 POST请求
因此,我们可以采用将GET请求和POST请求分开使用不同的序列化器的方式来解决这个问题.
# serializers.py文件
|
==> |
|
# views.py文件
1 2 3 4 5 6 7 8 9 10 11 | # view.py class BookModelViewSet(ModelViewSet): queryset = models.Book.objects. all () # serializer_class = serializers.BookModelSerializer def get_serializer_class( self ): # 重写了基类里面的get_serializer_class方法,这是固定写法 serializer_class = self .serializer_class if self .request.method in [ 'PUT' , 'POST' , 'PATCH' ]: serializers_class = serializers.PostModelSerializer if self .request.method = = 'GET' : serializers_class = serializers.GetModelSerializer return serializers_class |
如此,GET请求能够得到包括关联表的详细序列化信息,同时POST请求可以同时将数据添加到一对多关联表,以及自动创建的多对多关联表中,半自动创建的多对多关联表无法更新.
注意:
# 半自动创建的多对多关联表无法更新
# 无法同时添加多个值,比如一本书可以同时有多个主编,但是添加时,数据无法同时更新多个主编
另外同时满足一对多,多对多创建更新的方法:重写create方法和update方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | class BookModelSerializer(serializers.ModelSerializer): # 嵌套序列化信息 editors = EditorModelSerializer(many = True ) authors = AuthorModelSerializer(many = True ) publish = PublishModelSerializer() class Meta: model = models.Book fields = "__all__" def create( self , validated_data): editors_data = validated_data.pop( 'editors' ) authors_data = validated_data.pop( 'authors' ) publish_data = validated_data.pop( 'publish' ) publish = models.Publish.objects.get(name = publish_data.get( 'name' )) # 用fliter就是queryset,用get才是对象instance或obj # 添加一对多数据,和orm用法一致 book = models.Book.objects.create( * * validated_data,publish = publish) # 添加多对多字段数据,同样和orm用法一致 for editor_data in editors_data: editor = models.Editor.objects.get(name = editor_data.get( 'name' )) book.editors.add(editor) # 添加多对多字段数据,此处是半自动创建的多对多,无法使用add方法,用orm的create方法 for author_data in authors_data: author = models.Author.objects.get(name = author_data.get( 'name' )) models.Book2Author.objects.create(book = book,author = author) return book |
三 反向查找并序列化 _set
上述一对多,多对多序列化我们是从外键所在的表正向查找另外的表的数据,再进行序列化
如果我们想从一对多的一的一方反向查找多的一方并序列化,或者从多对多的非外键所在表反向查找,
如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | # editor表和book表示多对多,外键在book表中 # publish表和book表示一对多,外键在book表中 class EditorModelSerializer(serializers.ModelSerializer): # 反向查找 book_set = serializers.StringRelatedField(many = True ,read_only = True ) # book_set = serializers.PrimaryKeyRelatedField(many=True,read_only=True) class Meta: model = models.Editor # fields = ['name'] fields = "__all__" class PublishModelSerializer(serializers.ModelSerializer): # 反向查询 # book_set = serializers.StringRelatedField(many=True,read_only=True) book_set = serializers.PrimaryKeyRelatedField(many = True ,read_only = True ) class Meta: model = models.Publish fields = "__all__" class AuthorModelSerializer(serializers.ModelSerializer): # 反向查询 book_set = serializers.PrimaryKeyRelatedField(many = True ,read_only = True ) class Meta: model = models.Author fields = "__all__" # 使用orm语法的标识:"表名_set"即可反向查找 # 对自动创建的多对多和半自动创建的多对多同样适用 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了