数据库之间互相关联,一对多和多对多,继承ModelSerializer的序列化器内部代码需要定制

为了多对多关联,我们同时使用自动多对多建立了editor和book的关系,

半自动多对多建立了author和book的关系

# models.py文件

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

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

# 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请求

drf序列化choices中所有可选字段 drf 嵌套序列化_多对多

drf序列化choices中所有可选字段 drf 嵌套序列化_多对多_02

drf序列化choices中所有可选字段 drf 嵌套序列化_一对多_03

drf序列化choices中所有可选字段 drf 嵌套序列化_序列化_04

# 左侧为book列表,为列表套字典,右侧为单本书的详细信息,为字典格式

# 自动创建的多对多editors和半自动创建的authors都能够正确显示

# 但是,结果中并没有显示外键字段的详细信息,比如我同事需要authors,editors的姓名,id等信息

# 修改序列化器中的序列化深度depth属性=1就可以显示

# serializers.py
class BookModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.Book
        fields = "__all__"
        depth = 1
# 序列化深度depth属性能够将关联属性的表属性也显示序列化出来
# 引用序列化深度depth属性的字段序列化时会自动变为readonly,所以更新或创建时,不能使用该序列化器类

drf序列化choices中所有可选字段 drf 嵌套序列化_序列化_05

可以看出不管是自动化创建的多对多关系,还是半自动创建的多对多关系,以及一对多关系,此时都能够正确的在序列化信息显示.

但是,一旦使用了含有序列化深度depth属性的序列化器,那么引用改属性的字段就无法进行更新或者创建

如果不使用深度属性depth,有另外一种方法也可以得到多层序列化信息,如下

# 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两个类

 


class BookModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.Book
        fields = "__all__"
        depth = 1



==>


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

# 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方法:

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

上述一对多,多对多序列化我们是从外键所在的表正向查找另外的表的数据,再进行序列化

如果我们想从一对多的一的一方反向查找多的一方并序列化,或者从多对多的非外键所在表反向查找,

如下:

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