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文件

 将BookModelSerializer分拆为右边的GetModelSerializer,PostModelSerializer两个类
 
1
2
3
4
5
6
class BookModelSerializer(serializers.ModelSerializer):
 
    class Meta:
        model = models.Book
        fields = "__all__"
        depth = 1

  

==>
1
2
3
4
5
6
7
8
9
10
11
12
13
class GetModelSerializer(serializers.ModelSerializer):
 
    class Meta:
        model = models.Book
        fields = "__all__"
        depth = 1
 
 
class PostModelSerializer(serializers.ModelSerializer):
 
    class Meta:
        model = models.Book
        fields = "__all__"

  

# 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"即可反向查找
# 对自动创建的多对多和半自动创建的多对多同样适用

  

 

posted @   EricYJChung  阅读(2864)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示