Django · DRF入门 · 模型类序列化器 | 嵌套序列化器 | 外键约束复杂字段的查询处理

DRF入门 · 模型类序列化器 | 嵌套序列化器 | 外键约束字段的查询处理

1. ModelSerializer模型类序列化器

在上一篇文章中,我们讲解了基类序列化器Serializer的用法,以及基本字段的定义,但是其实对于大部分的字段,我们在定义模型类的时候,就已经对其规则进行了相关定义,序列化的时候只要按照我模型的参数和数据类型做验证就够了,完全没必要再重新定义字段类型等杂七杂八的参数,而ModelSerializer序列化器就来了,它和我们的Model高度关联,只要定义好了Model,那后面的基本就不用管了。

models.py

from django.db import models

class Author(models.Model):
    class Meta:
        verbose_name = '作者信息表'
    name = models.CharField(max_length=128, verbose_name='作者姓名')
    introduce = models.CharField(max_length=512, verbose_name='作者简介')


class Book(models.Model):
    class Meta:
        verbose_name = '图书信息表'
    name = models.CharField(max_length=128, verbose_name='书名')
    pub_date = models.DateField(verbose_name='发布日期')
    # Book表中author字段与作者表形成外键约束
    author = models.ForeignKey(to=Author, on_delete=models.DO_NOTHING, verbose_name='作者')
    price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='价格')

如果按照我们之前的普通序列化器的用法,那这个就太麻烦了,下面还得给这些字段,让他再定义验证的格式

1.1 ModelSerializer和基类Serializer复杂度对比

使用旧方法serializers.Serializers

class AuthorSerializer(serializers.ModelSerializer):
    name = serializers.CharField(max_length=128, label='作者姓名')
    introduce = serializers.CharField(max_length=128,label='作者简介')

    def create(self, validated_data):
        """这里还得定义create"""
        pass
    def update(self, instance, validated_data):
        
        """这里还得定义update"""
        pass

可以发现啊,旧方法,非常非常的麻烦,还得处理保存的关系,下来,我们看看ModelSerializer有多方便

使用ModelSerializer

class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = "__all__"

好了,就这四行代码,实现了上面几十行代码的功能,就这么简单!

那么大家肯定一脸懵逼,这怎么实现的,这代码什么意思呢?

1.2 ModelSerializer用法

ModelSerializer详解

class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        """
            ModelSerializer类的Meta类,用于指定序列化器类对应的模型类以及序列化的字段
        """
        model = Author  # 指定模型类
        #### 指定需要序列化的字段 ####
        # 1. 序列化全部字段fields="__all__",则表示序列化所有字段
        fields = "__all__"

        # 2. 序列化指定字段
        # fields = ["name", "price"] # 指定序列化的字段,如果是列表/元组,则表示序列化指定的字段
		# 元组或者列表都可以
        
        # 3. 排除字段(与fields相反)
        # exclude = ["id","pub_date"] # 表示除了id和pub_date字段,都需要序列化

        
### 现在有个需求,前端小姐姐希望我返回的日期字段是"date"而不是pub_date,也就是说,我现在数据库model模型关联的字段名是pub_date,但是前端小姐姐,希望我返回一个date,那我该怎么处理??
# 非常简单,对于需要单独处理的字段,我们可以单独拎出来


class BookSerializer(serializers.ModelSerializer):
    date = serializers.DateField(source='pub_date') # 指定date字段,来源于pub_date字段
    class Meta:
        model = Book
        exclude = ["pub_date","id"] # 因为我们自己单独定义了date字段,所以原本返回的pub_date,这里直接排除掉

# 此时我们请求下看看

[
	{
		"date": "2023-08-03",
		"name": "时间简史",
		"price": "50.00",
		"author": 1
	}
]
# 返回的就是date,并且没有pub_date

2. 处理外键约束字段的显示

2.1 需求一 分析

现在我的Book表和Author表之间的author字段关联,所以序列化的时候,可以发现,返回的author字段也是id,但是我现在的需求是,你不仅要给我返回一个author的name,你还得吧id给我,也就是说你得把id和name都得给我。

# 原本的返回
{
"date": "2023-08-03",
"name": "时间简史",
"price": "50.00",
"author": 1
}
# 希望的返回
{
"date": "2023-08-03",
"name": "时间简史",
"price": "50.00",
"author": "霍金""author_id": 1"
}

好,这是个好需求, 那该怎么实现呢?

2.2 SerializerMethodField引入

DRF中为我们提供了SerializerMethodField,听名字就知道方法字段序列化器,也就是说,我们一个字段的值,可以通过指定的方法去返回,比如我定义一个当前时间的字段,我就需要让他返回一个获取当前时间的函数。

DRF在使用SerializerMethodField字段的指定方法时,会将此时序列化器中的model对象,传入方法中。

使用SerializerMethodField实现2.1需求

from rest_framework import serializers
from BookManage.models import *
class BookSerializer(serializers.ModelSerializer):
    author = serializers.SerializerMethodField()
    # 如果MethodField不指定method_name,那函数名必须命名为 "get{变量名}"的形式
    author_id = serializers.SerializerMethodField(method_name='xxxx') # 指定方法名=xxxx
    # 指定了方法名,则会通过getattr进行查找
    def get_author(self,book):
        """
            没有指定author字段的method_name,函数必须按get_author命令
            在调用查询字段方法时,会将当时的model对象传入
        :param book: book的model对象
        """
        return book.author.name
    def xxxx(self,book):
        """
        	关联的时author_id字段
        """
        return book.author.id

返回结果示例:

[
	{
		"id": 1,
		"author": "霍金",
		"author_id": 1,
		"name": "时间简史",
		"pub_date": "2023-08-03",
		"price": "50.00"
	}
]

3. 处理外键约束-使用嵌套序列化器

3.1 需求二 分析

现在我的需求变了,我要查的是,作者的全部信息,并且需要单独给我拎出来,变成一个嵌套JSON数据,你该怎么处理?

如下是示例:

[
	{
		"id": 1,
		"name": "时间简史",
		"pub_date": "2023-08-03",
		"author": {
			"id": 1,
			"name": "霍金",
			"introduce": "斯蒂芬·威廉·霍金(Stephen William Hawking,1942年1月8日—2018年3月14日),男,出生于英国牛津,英国剑桥大学著名物理学家,现代最伟大的物理学家之一、20世纪享有国际盛誉的伟人之一。"
		},
		"price": "50.00"
	}
]

也许有人说了,我和2.1一样,把author单独拎出来,然后定义一个MethodField字段,然后让方法返回一个字典,是,你说的也可以做到,但是如果我这个作者信息,有几十个字段呢?我的作者信息非常的杂乱无章,你怎么办?不可能一个个去返回把?

所以,DRF为了方便,便提供了嵌套序列化器

首先,创建一个新的序列化器来表示作者信息:

from rest_framework import serializers
from .models import Author, Book

class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = ('id', 'name', 'introduce') # 定义作者需要返回的字段

接下来,在Book序列化器中,使用嵌套序列化器来表示作者信息:

class BookSerializer(serializers.ModelSerializer):
    # 使用AuthorSerializer作为author字段的嵌套序列化器
    author = AuthorSerializer()
    # 自己额外定义的字段,一定不能排除,如果排除就会报错!
    class Meta:
        model = Book
        fields = ('id', 'name', 'pub_date', 'author', 'price')

此时我们进行GET请求的时候,可以发现,就已经返回了我们想要的数据。

[
	{
		"id": 1,
		"name": "时间简史",
		"pub_date": "2023-08-03",
		"author": {
			"id": 1,
			"name": "霍金",
			"introduce": "斯蒂芬·威廉·霍金(Stephen William Hawking,1942年1月8日—2018年3月14日),男,出生于英国牛津,英国剑桥大学著名物理学家,现代最伟大的物理学家之一、20世纪享有国际盛誉的伟人之一。"
		},
		"price": "50.00"
	}
]

3.2 需求二的坑!

如果你按照3.1的方式进行实现,那的确没问题,返回的数据也没有错误,但是你会发现,当你使用POST请求去创建新数据的时候,就会有问题了!

{
    "name":"果壳中的宇宙",
    "pub_date":"2005-08-03",
    "author":1,
    "price":66
}
# 传入创建信息
# 报错:
{
	"author": {
		"non_field_errors": [
			"Invalid data. Expected a dictionary, but got int." # 期望得到一个字典?
		]
	}
}

其实,如果按这种方式处理,那你创建的时候,作者也得传入一个字典才能创建成功

{
    "name":"果壳中的宇宙",
    "pub_date":"2005-08-03",
    "author": {
        "id": 1,
        "name":"霍金",
        "introduce":"xxxx",
        },
    "price":66
}

但是,如果我非要实现这样的查询呢??

下面我就来演示,如何实现外键约束且读写不同的序列化器

4. 外键约束且读写不同的复杂序列化器

需求:

1. 查询信息时,可以返回以下信息:

{
	"id": 1,
	"name": "时间简史",
	"pub_date": "2023-08-03",
    // 查询时,返回作者的完整字段信息,字典类型
	"author": {
		"id": 1,
		"name": "霍金",
		"introduce": "斯蒂芬·威廉·霍金(Stephen William Hawking,1942年1月8日—2018年3月14日),男,出生于英国牛津,英国剑桥大学著名物理学家,现代最伟大的物理学家之一、20世纪享有国际盛誉的伟人之一。"
	},
	"price": "50.00",
}

2. 创建信息的时候,只需要传入author_id就能够实现创建

{
    "name":"大设计",
    "pub_date":"2008-08-03",
    "author_id":88, //只需要传入author_id就可以
    "price":33
}

4.1 实现方法 PrimaryKeyRelatedField+嵌套序列化器

PrimaryKeyRelatedField是序列化器字段中,主键相关字段

class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = ('id', 'name', 'introduce')


class BookSerializer(serializers.ModelSerializer):
    # 使用AuthorSerializer作为author字段的嵌套序列化器
    author = AuthorSerializer(read_only=True)
 	# 使用主键字段类型
    author_id = serializers.PrimaryKeyRelatedField(queryset=Author.objects.all(), source='author', write_only=True)
	# 并且将author_id设置为"只写",也就是读取的时候不会返回!
    class Meta:
        model = Book
        fields = ['id', 'name', 'pub_date', 'author', 'price', 'author_id']

PrimaryKeyRelatedField介绍

PrimaryKeyRelatedField(queryset=Author.objects.all(), source='author', write_only=True)

queryset:
	关联的主表的模型类对象QuerySet
soure:
	从表的关联键的名称
	
案例:
	serializers.PrimaryKeyRelatedField(queryset=Author.objects.all(), source='author', write_only=True)
这里当前序列化器的model是Book(从表)
所以传入就是上面的参数

5. 其他更加复杂类型的序列化处理后续会不断讲解!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值