手摸手,带你用Django REST Framework撸接口系列二(序列化器篇)

序列化的作用就是进行数据校验,对数据对象进行转换的过程,其核心原理就是讲数据库数据序列化为前端所需要的格式并返回;将前端发送的数据反序列化为模型类对象,并保存在数据库中。

需要序列化字段

  • 在不做任何处理的情况下,我们定义的类里面的字段必须在model类中必须存在该字段
  • 参与序列化的属性名必须与model类的属性相同
  • 只出现在序列化中不出现在反序列化中我们要加只读属性read_only=True
  • 如果我们Serializer类中定义字段类型为SerializerMethodField及自定义字段类型不用遵守类里面的字段必须在model类中必须存在该字段

不需要序列化字段

  • 不需要序列化的属性字段在序列化类中不需要声明
  • 不需要序列化的属性字段在序列化类中设置只写属性write_only=True

在开发REST API的视图中,虽然每个视图具体操作的数据不同,但增、删、改、查的实现流程基本套路化,所以这部分代码也是可以复用简化编写的:

增:校验请求数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回

删:判断要删除的数据是否存在 -> 执行数据库删除

改:判断要修改的数据是否存在 -> 校验请求的数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回

查:查询数据库 -> 将数据序列化并返回

不同情况下用的序列化方法

  • 单表:最简单定义fileds字段
  • ForeignKey:基于source参数链表操作
  • ManyToManyField:基于SerializerMethodField+自定义一个钩子方法。

注意:choice显示中文可以使用source字段 “get_字段名_display

定义serializer

定义方法

Django REST framework中的Serializer使用类来定义,须继承自rest_framework.serializers.Serializer

例如,我们已有了一个数据库模型类Article

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Article(models.Model):
"""文章模型"""
# 文章状态
STATUS_CHOICES = (
('p', '发布'),
('d', '草稿'),
)

title = models.CharField(verbose_name='标题', max_length=90, db_index=True)
body = models.TextField(verbose_name='内容', blank=True)
author = models.ForeignKey(User, verbose_name='作者', on_delete=models.CASCADE, related_name='articles')
status = models.CharField(verbose_name='状态', max_length=1, choices=STATUS_CHOICES, default='s', null=True, blank=True)
created_time = models.DateTimeField(verbose_name='发布日期', auto_now_add=True)

def __str__(self):
"""返回文章标题"""
return self.title

class Meta:
# 排序,主键倒序
ordering = ['-pk']
verbose_name = "文章"
verbose_name_plural = verbose_name

我们想为这个模型类提供一个序列化器,可以定义如下:

1
2
3
4
5
6
7
8
class ArticleSerializer(serializers.Serializer):
"""文章序列化类"""
id = serializers.IntegerField(label='ID', read_only=True)
title = serializers.CharField(label='标题')
body = serializers.CharField(label='内容')
author = serializers.CharField(label='作者')
status = serializers.CharField(label='状态')
created_time = serializers.DateTimeField(label='发布时间')

或者还可以创建基于模型的序列化器

1
2
3
4
5
6
7
class ArticleSerializer(serializers.ModelSerializer):
"""文章序列化类"""

class Meta:
model = Article
fields = "__all__"
read_only_fields = ('id', 'created_time', 'author')

注意:serializer不是只能为数据库模型类定义,也可以为非数据库模型类的数据定义。serializer是独立于数据库之外的存在。

字段与选项

常用字段类型

字段字段构造方式
BooleanFieldBooleanField()
NullBooleanFieldNullBooleanField()
CharFieldCharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
EmailFieldEmailField(max_length=None, min_length=None, allow_blank=False)
RegexFieldRegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugFieldSlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
URLFieldURLField(max_length=200, min_length=None, allow_blank=False)
UUIDFieldUUIDField(format=’hex_verbose’) format: 1) 'hex_verbose'"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex'"5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
IPAddressFieldIPAddressField(protocol=’both’, unpack_ipv4=False, **options)
IntegerFieldIntegerField(max_value=None, min_value=None)
FloatFieldFloatField(max_value=None, min_value=None)
DecimalFieldDecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
DateTimeFieldDateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
DateFieldDateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeFieldTimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationFieldDurationField()
ChoiceFieldChoiceField(choices) choices与Django的用法相同
MultipleChoiceFieldMultipleChoiceField(choices)
FileFieldFileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageFieldImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListFieldListField(child=, min_length=None, max_length=None)
DictFieldDictField(child=)

选项参数

参数名称作用
max_length最大长度
min_lenght最小长度
allow_blank是否允许为空
trim_whitespace是否截断空白字符
max_value最小值
min_value最大值

通用参数

参数名称说明
read_only表明该字段仅用于序列化输出,默认False
write_only表明该字段仅用于反序列化输入,默认False
required表明该字段在反序列化时必须输入,默认True
default反序列化时使用的默认值
allow_null表明该字段是否允许传入None,默认False
validators该字段使用的验证器
error_messages包含错误编号与错误信息的字典
label用于HTML展示API页面时,显示的字段名称
help_text用于HTML展示API页面时,显示的字段帮助提示信息

创建Serializer对象

定义好Serializer类后,就可以创建Serializer对象了。

Serializer的构造方法为:

1
Serializer(instance=None, data=empty, **kwarg)

说明:

1)用于序列化时,将模型类对象传入instance参数

2)用于反序列化时,将要被反序列化的数据传入data参数

3)除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如

1
serializer = AccountSerializer(account, context={'request': request})

通过context参数附加的数据,可以通过Serializer对象的context属性获取。

前提准备

我们为了更直观的说明序列器的使用方式,为大家准备好了两个模型和初始的序列化类如下:

模型:

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
from django.contrib.auth.models import User
from django.db import models


class Article(models.Model):
"""文章模型"""
# 文章状态
STATUS_CHOICES = (
('p', '发布'),
('d', '草稿'),
)

title = models.CharField(verbose_name='标题', max_length=90, db_index=True)
body = models.TextField(verbose_name='内容', blank=True)
author = models.ForeignKey(User, verbose_name='作者', on_delete=models.CASCADE, related_name='articles')
status = models.CharField(verbose_name='状态', max_length=1, choices=STATUS_CHOICES, default='s', null=True, blank=True)
created_time = models.DateTimeField(verbose_name='发布日期', auto_now_add=True)

def __str__(self):
"""返回文章标题"""
return self.title

class Meta:
# 排序,主键倒序
ordering = ['-pk']
verbose_name = "文章"
verbose_name_plural = verbose_name

序列化类:

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/3/23 17:32
# @Author : 托小尼
# @Email : 646547989@qq.com
# @URI : https://www.diandian100.cn
# @File : article.py
from django.contrib.auth.models import User
from rest_framework import serializers

from api.models import Article


class ArticleSerializer(serializers.ModelSerializer):
"""文章序列化类"""

class Meta:
model = Article
fields = "__all__"
read_only_fields = ('id', 'created_time', 'author')

def create(self, validated_data):
"""返回创建的新文章实例"""
return Article.objects.create(**validated_data)

def update(self, instance, validated_data):
"""更新已有的文章实例并返回"""
instance.title = validated_data.get('title', instance.title)
instance.body = validated_data.get('body', instance.body)
instance.status = validated_data.get('status', instance.status)
instance.save()
return instance


class UserSerializer(serializers.ModelSerializer):
"""用户序列化类"""
class Meta:
model = User
fields = "__all__"

序列化的使用

如果我们想要使用序列化器对应的是Django的模型类,DRF为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类。

ModelSerializer与常规的Serializer相同,但提供了:

  • 基于模型类自动生成一系列字段
  • 基于模型类自动为Serializer生成validators,比如unique_together
  • 包含默认的create()和update()的实现

基于以上原因我们以下都以ModelSerializer为例来进行说明

序列化基本数据

序列化单条数据

1
2
3
4
5
6
# 查询主键为2的文章
article_obj = Article.objects.get(pk=2)
# 序列化该对象
serializers = ArticleSerializer(article_obj)
# 返回给前端
return Response(serializers.data)

输出结果:

1
2
3
4
5
6
7
8
{
"id": 2,
"author": 3,
"title": "关于铲除非法社会组织滋生土壤 净化社会组织生态空间的通知",
"body": "会组织要依法登记、依法运作,这是世界各国通行的做法。我国是法治国家,支持合法登记的社会组织依法运作、维护合法登记的社会组织正当权益,是我们的一贯方针,同时,打击整治非法社会组织也是我们的一贯方针。多年来,各地各部门对非法社会组织持续保持高压态势,非法社会组织活动明显减少,社会组织生态空间日渐清朗。但近年来受利益驱动,一些非法社会组织不断变换手法,以假乱真、招摇撞骗,一些地方非法社会组织又有所滋长。",
"status": "p",
"created_time": "2021-03-23T09:47:43.792678Z"
}

序列化多条数据

1
2
3
4
5
6
# 查询所有文章
articles = Article.objects.all()
# 序列化该数据集(多条数据要加many=True)
serializers = ArticleSerializer(articles, many=True)
# 返回给前端
return Response(serializers.data)

输出结果:

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
[
{
"id": 10,
"author": 1,
"title": "fff少时诵诗书所所所所所所所所所所所所所所所所",
"body": "沙发撒的发是的发送到发生发",
"status": null,
"created_time": "2021-03-24T05:26:21.521804Z"
},
{
"id": 8,
"author": 1,
"title": "Django是如何写出来的",
"body": "发顺丰的",
"status": "d",
"created_time": "2021-03-24T02:32:03.208494Z"
},
{
"id": 6,
"author": 1,
"title": "我是一只小小鸟",
"body": "weasfasdf时发生地方",
"status": "d",
"created_time": "2021-03-24T02:21:26.062896Z"
},
{
"id": 2,
"author": 3,
"title": "关于铲除非法社会组织滋生土壤 净化社会组织生态空间的通知",
"body": "会组织要依法登记、依法运作,这是世界各国通行的做法。我国是法治国家,支持合法登记的社会组织依法运作、维护合法登记的社会组织正当权益,是我们的一贯方针,同时,打击整治非法社会组织也是我们的一贯方针。多年来,各地各部门对非法社会组织持续保持高压态势,非法社会组织活动明显减少,社会组织生态空间日渐清朗。但近年来受利益驱动,一些非法社会组织不断变换手法,以假乱真、招摇撞骗,一些地方非法社会组织又有所滋长。",
"status": "p",
"created_time": "2021-03-23T09:47:43.792678Z"
},
{
"id": 1,
"author": 2,
"title": "中方制裁名单上排第一的欧洲议员是谁?",
"body": "22日晚间,中国外交部宣布,决定对欧方严重损害中方主权和利益、恶意传播谎言和虚假信息的10名人员和4个实体实施制裁,以对欧盟以涉疆事务为由对中方个人和实体施加制裁进行反制。在外交部公布的制裁名单上,排在首位的是欧洲议会议员彼蒂科菲尔,他最近曾多次无端攻击中国的新疆政策,并以此为由鼓动对中国官员的制裁和阻挠中欧全面投资协定。",
"status": "p",
"created_time": "2021-03-23T09:46:50.668004Z"
}
]

SerializerMethodField

用户根据该字段的回调函数自定义输出结果

获取所有文章列表及文章对应的用户id,根据用户id输出用户对应的自定义身份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ArticleSerializer(serializers.ModelSerializer):
"""文章序列化类"""
full_status = serializers.ReadOnlyField(source="get_status_display")
author_type = serializers.SerializerMethodField()

def get_author_type(self, obj):
# 根据用户id判断用户身份并输出
if obj.author_id > 1:
return "普通用户"
return "超级管理员"

class Meta:
model = Article
fields = "__all__"
read_only_fields = ('id', 'created_time', 'author')

输出结果

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
[
{
"id": 10,
"full_status": null,
"author_type": "超级管理员",
"title": "fff少时诵诗书所所所所所所所所所所所所所所所所",
"body": "沙发撒的发是的发送到发生发",
"status": null,
"created_time": "2021-03-24T05:26:21.521804Z",
"author": 1
},
{
"id": 8,
"full_status": "草稿",
"author_type": "超级管理员",
"title": "Django是如何写出来的",
"body": "发顺丰的",
"status": "d",
"created_time": "2021-03-24T02:32:03.208494Z",
"author": 1
},
{
"id": 6,
"full_status": "草稿",
"author_type": "超级管理员",
"title": "我是一只小小鸟",
"body": "weasfasdf时发生地方",
"status": "d",
"created_time": "2021-03-24T02:21:26.062896Z",
"author": 1
},
{
"id": 2,
"full_status": "发布",
"author_type": "普通用户",
"title": "关于铲除非法社会组织滋生土壤 净化社会组织生态空间的通知",
"body": "会组织要依法登记、依法运作,这是世界各国通行的做法。我国是法治国家,支持合法登记的社会组织依法运作、维护合法登记的社会组织正当权益,是我们的一贯方针,同时,打击整治非法社会组织也是我们的一贯方针。多年来,各地各部门对非法社会组织持续保持高压态势,非法社会组织活动明显减少,社会组织生态空间日渐清朗。但近年来受利益驱动,一些非法社会组织不断变换手法,以假乱真、招摇撞骗,一些地方非法社会组织又有所滋长。",
"status": "p",
"created_time": "2021-03-23T09:47:43.792678Z",
"author": 3
},
{
"id": 1,
"full_status": "发布",
"author_type": "普通用户",
"title": "中方制裁名单上排第一的欧洲议员是谁?",
"body": "22日晚间,中国外交部宣布,决定对欧方严重损害中方主权和利益、恶意传播谎言和虚假信息的10名人员和4个实体实施制裁,以对欧盟以涉疆事务为由对中方个人和实体施加制裁进行反制。在外交部公布的制裁名单上,排在首位的是欧洲议会议员彼蒂科菲尔,他最近曾多次无端攻击中国的新疆政策,并以此为由鼓动对中国官员的制裁和阻挠中欧全面投资协定。",
"status": "p",
"created_time": "2021-03-23T09:46:50.668004Z",
"author": 2
}
]

指定source来源

查询文章对应的作者用户名和文章状态中文显示

新建两个可读字段author和status字段,用以覆盖原来Article模型默认的字段,其中指定author字段的来源(source)为单个author对象的username,status字段为get_status_display方法返回的完整状态。

1
2
3
4
5
6
7
8
9
class ArticleSerializer(serializers.ModelSerializer):
"""文章序列化类"""
full_status = serializers.ReadOnlyField(source="get_status_display")
author = serializers.ReadOnlyField(source="author.username")

class Meta:
model = Article
fields = "__all__"
read_only_fields = ('id', 'created_time', 'author')

输出:

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
[
{
"id": 10,
"full_status": null,
"author": "admin",
"title": "fff少时诵诗书所所所所所所所所所所所所所所所所",
"body": "沙发撒的发是的发送到发生发",
"status": null,
"created_time": "2021-03-24T05:26:21.521804Z"
},
{
"id": 8,
"full_status": "草稿",
"author": "admin",
"title": "Django是如何写出来的",
"body": "发顺丰的",
"status": "d",
"created_time": "2021-03-24T02:32:03.208494Z"
},
{
"id": 6,
"full_status": "草稿",
"author": "admin",
"title": "我是一只小小鸟",
"body": "weasfasdf时发生地方",
"status": "d",
"created_time": "2021-03-24T02:21:26.062896Z"
},
{
"id": 2,
"full_status": "发布",
"author": "member1",
"title": "关于铲除非法社会组织滋生土壤 净化社会组织生态空间的通知",
"body": "会组织要依法登记、依法运作,这是世界各国通行的做法。我国是法治国家,支持合法登记的社会组织依法运作、维护合法登记的社会组织正当权益,是我们的一贯方针,同时,打击整治非法社会组织也是我们的一贯方针。多年来,各地各部门对非法社会组织持续保持高压态势,非法社会组织活动明显减少,社会组织生态空间日渐清朗。但近年来受利益驱动,一些非法社会组织不断变换手法,以假乱真、招摇撞骗,一些地方非法社会组织又有所滋长。",
"status": "p",
"created_time": "2021-03-23T09:47:43.792678Z"
},
{
"id": 1,
"full_status": "发布",
"author": "member",
"title": "中方制裁名单上排第一的欧洲议员是谁?",
"body": "22日晚间,中国外交部宣布,决定对欧方严重损害中方主权和利益、恶意传播谎言和虚假信息的10名人员和4个实体实施制裁,以对欧盟以涉疆事务为由对中方个人和实体施加制裁进行反制。在外交部公布的制裁名单上,排在首位的是欧洲议会议员彼蒂科菲尔,他最近曾多次无端攻击中国的新疆政策,并以此为由鼓动对中国官员的制裁和阻挠中欧全面投资协定。",
"status": "p",
"created_time": "2021-03-23T09:46:50.668004Z"
}

关联对象嵌套序列化

如果我们要查询发布该文章的用户信息要怎么做呢?如果要查询所有用户和他发布的所有文章又该如何做呢?这就需要用到关联对象嵌套序列化了。

PrimaryKeyRelatedField

该字段被序列化为关联对象的主键

参数:

  • queryset - 验证字段输入时用于模型实例查询的查询集。必须显式地设置查询集,或设置 read_only=True
  • many - 如果应用于一对多关系,则应将此参数设置为 True
  • allow_null - 如果设置为 True,那么该字段将接受 None 值或可为空的关系的空字符串。默认为 False
  • pk_field - 设置为一个字段来控制主键值的序列化/反序列化。例如, pk_field=UUIDField(format='hex') 会将 UUID 主键序列化为其紧凑的十六进制表示形式。

查询所有用户及其发表过的文章主键,我们的用户序列化器应该如下定义:

1
2
3
4
5
6
7
8
9
class UserSerializer(serializers.ModelSerializer):
"""用户序列化类"""
# Article数据模型已经为关联用户定义了反查属性related_name='articles',所以article_set弃用
# article_set = serializers.PrimaryKeyRelatedField(read_only=True, many=True)
articles = serializers.PrimaryKeyRelatedField(read_only=True, many=True)

class Meta:
model = User
fields = "__all__"

查询结果:

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
[
{
"id": 1,
"articles": [
10,
8,
6
],
"password": "pbkdf2_sha256$216000$H7uXzmEmLPFr$RaAYcUf6g8tgV9hLytiySyBp3Fo+LP52SZnCVfrYxTk=",
"last_login": "2021-03-23T09:19:42.312394Z",
"is_superuser": true,
"username": "admin",
"first_name": "",
"last_name": "",
"email": "admin@qq.com",
"is_staff": true,
"is_active": true,
"date_joined": "2021-03-23T09:18:58.092435Z",
"groups": [],
"user_permissions": []
},
{
"id": 2,
"articles": [
1
],
"password": "pbkdf2_sha256$216000$rUpAo2698YTh$lM8HUQMlmknCF61aGtsmPWXcn5yLLQPTgvpspT19yiU=",
"last_login": null,
"is_superuser": false,
"username": "member",
"first_name": "",
"last_name": "",
"email": "",
"is_staff": false,
"is_active": true,
"date_joined": "2021-03-23T09:45:00.549553Z",
"groups": [],
"user_permissions": []
},
{
"id": 3,
"articles": [
2
],
"password": "pbkdf2_sha256$216000$0h2Wxbeboony$xgp2pvwfBMpJ0KmhpxhTutwQFU/loC3tvtqGMSuCm2o=",
"last_login": null,
"is_superuser": false,
"username": "member1",
"first_name": "",
"last_name": "",
"email": "",
"is_staff": false,
"is_active": true,
"date_joined": "2021-03-23T09:45:18.034139Z",
"groups": [],
"user_permissions": []
}
]

StringRelatedField

此字段将被序列化为关联对象的字符串表示方式(即__str__方法的返回值)

参数:

  • many - 如果是一对多的关系,就将此参数设置为 True.

查询所有用户及其发表的文章标题

1
2
3
4
5
6
7
class UserSerializer(serializers.ModelSerializer):
"""用户序列化类"""
articles = serializers.StringRelatedField(read_only=True, many=True)

class Meta:
model = User
fields = "__all__"

输出结果:

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
[
{
"id": 1,
"articles": [
"fff少时诵诗书所所所所所所所所所所所所所所所所",
"Django是如何写出来的",
"我是一只小小鸟"
],
"password": "pbkdf2_sha256$216000$H7uXzmEmLPFr$RaAYcUf6g8tgV9hLytiySyBp3Fo+LP52SZnCVfrYxTk=",
"last_login": "2021-03-23T09:19:42.312394Z",
"is_superuser": true,
"username": "admin",
"first_name": "",
"last_name": "",
"email": "admin@qq.com",
"is_staff": true,
"is_active": true,
"date_joined": "2021-03-23T09:18:58.092435Z",
"groups": [],
"user_permissions": []
},
{
"id": 2,
"articles": [
"中方制裁名单上排第一的欧洲议员是谁?"
],
"password": "pbkdf2_sha256$216000$rUpAo2698YTh$lM8HUQMlmknCF61aGtsmPWXcn5yLLQPTgvpspT19yiU=",
"last_login": null,
"is_superuser": false,
"username": "member",
"first_name": "",
"last_name": "",
"email": "",
"is_staff": false,
"is_active": true,
"date_joined": "2021-03-23T09:45:00.549553Z",
"groups": [],
"user_permissions": []
},
{
"id": 3,
"articles": [
"关于铲除非法社会组织滋生土壤 净化社会组织生态空间的通知"
],
"password": "pbkdf2_sha256$216000$0h2Wxbeboony$xgp2pvwfBMpJ0KmhpxhTutwQFU/loC3tvtqGMSuCm2o=",
"last_login": null,
"is_superuser": false,
"username": "member1",
"first_name": "",
"last_name": "",
"email": "",
"is_staff": false,
"is_active": true,
"date_joined": "2021-03-23T09:45:18.034139Z",
"groups": [],
"user_permissions": []
}
]

HyperlinkedRelatedField

HyperlinkedRelatedField 用于使用超链接来表示关系,必须指定视图别名view_name

注意:该字段是为映射到接受单个 URL 关键字参数的 URL 的对象而设计的,如使用 lookup_fieldlookup_url_kwarg 参数设置的对象。

这适用于包含单个主键或 slug 参数作为 URL 一部分的 URL。

如果需要更复杂的超链接表示,你需要自定义该字段,稍后会详解。

参数

  • view_name - 用作关系目标的视图名称。如果你使用的是标准路由器类,则这将是一个格式为 <modelname>-detail 的字符串。必填.
  • queryset - 验证字段输入时用于模型实例查询的查询集。必须显式地设置查询集,或设置 read_only=True
  • many - 如果应用于一对多关系,则应将此参数设置为 True
  • allow_null - 如果设置为 True,那么该字段将接受 None 值或可为空的关系的空字符串。默认为 False
  • lookup_field - 用于查找的目标字段。对应于引用视图上的 URL 关键字参数。默认是 'pk'.
  • lookup_url_kwarg - 与查找字段对应的 URL conf 中定义的关键字参数的名称。默认使用与 lookup_field 相同的值。
  • format - 如果使用格式后缀,则超链接字段将使用与目标相同的格式后缀,除非使用 format 参数进行覆盖。

查询所用用户及其发表的文章超链接

1
2
3
4
5
6
7
class UserSerializer(serializers.ModelSerializer):
"""用户序列化类"""
articles = serializers.HyperlinkedRelatedField(read_only=True, many=True, view_name="api:article-detail")

class Meta:
model = User
fields = "__all__"

输出结果:

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
[
{
"id": 1,
"articles": [
"http://localhost:8000/api/articles/10/",
"http://localhost:8000/api/articles/8/",
"http://localhost:8000/api/articles/6/"
],
"password": "pbkdf2_sha256$216000$H7uXzmEmLPFr$RaAYcUf6g8tgV9hLytiySyBp3Fo+LP52SZnCVfrYxTk=",
"last_login": "2021-03-23T09:19:42.312394Z",
"is_superuser": true,
"username": "admin",
"first_name": "",
"last_name": "",
"email": "admin@qq.com",
"is_staff": true,
"is_active": true,
"date_joined": "2021-03-23T09:18:58.092435Z",
"groups": [],
"user_permissions": []
},
{
"id": 2,
"articles": [
"http://localhost:8000/api/articles/1/"
],
"password": "pbkdf2_sha256$216000$rUpAo2698YTh$lM8HUQMlmknCF61aGtsmPWXcn5yLLQPTgvpspT19yiU=",
"last_login": null,
"is_superuser": false,
"username": "member",
"first_name": "",
"last_name": "",
"email": "",
"is_staff": false,
"is_active": true,
"date_joined": "2021-03-23T09:45:00.549553Z",
"groups": [],
"user_permissions": []
},
{
"id": 3,
"articles": [
"http://localhost:8000/api/articles/2/"
],
"password": "pbkdf2_sha256$216000$0h2Wxbeboony$xgp2pvwfBMpJ0KmhpxhTutwQFU/loC3tvtqGMSuCm2o=",
"last_login": null,
"is_superuser": false,
"username": "member1",
"first_name": "",
"last_name": "",
"email": "",
"is_staff": false,
"is_active": true,
"date_joined": "2021-03-23T09:45:18.034139Z",
"groups": [],
"user_permissions": []
}
]

SlugRelatedField

SlugRelatedField 用于使用目标上的字段来表示关系。

默认情况下,该字段是可读写的,但您可以使用 read_only 标志更改此行为。

SlugRelatedField 用作读写字段时,通常需要确保 slug 字段与 unique=True 的模型字段相对应。

参数

  • slug_field - 用来表示目标的字段。这应该是唯一标识给定实例的字段。例如, username必填
  • queryset - 验证字段输入时用于模型实例查询的查询集。必须显式地设置查询集,或设置 read_only=True
  • many - 如果应用于一对多关系,则应将此参数设置为 True
  • allow_null - 如果设置为 True,那么该字段将接受 None 值或可为空的关系的空字符串。默认为 False

查询所有用户及其发表的文章内容列表

1
2
3
4
5
6
7
class UserSerializer(serializers.ModelSerializer):
"""用户序列化类"""
articles = serializers.SlugRelatedField(read_only=True, many=True, slug_field="body")

class Meta:
model = User
fields = "__all__"

输出结果:

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
[
{
"id": 1,
"articles": [
"沙发撒的发是的发送到发生发",
"发顺丰的",
"weasfasdf时发生地方"
],
"password": "pbkdf2_sha256$216000$H7uXzmEmLPFr$RaAYcUf6g8tgV9hLytiySyBp3Fo+LP52SZnCVfrYxTk=",
"last_login": "2021-03-23T09:19:42.312394Z",
"is_superuser": true,
"username": "admin",
"first_name": "",
"last_name": "",
"email": "admin@qq.com",
"is_staff": true,
"is_active": true,
"date_joined": "2021-03-23T09:18:58.092435Z",
"groups": [],
"user_permissions": []
},
{
"id": 2,
"articles": [
"22日晚间,中国外交部宣布,决定对欧方严重损害中方主权和利益、恶意传播谎言和虚假信息的10名人员和4个实体实施制裁,以对欧盟以涉疆事务为由对中方个人和实体施加制裁进行反制。在外交部公布的制裁名单上,排在首位的是欧洲议会议员彼蒂科菲尔,他最近曾多次无端攻击中国的新疆政策,并以此为由鼓动对中国官员的制裁和阻挠中欧全面投资协定。"
],
"password": "pbkdf2_sha256$216000$rUpAo2698YTh$lM8HUQMlmknCF61aGtsmPWXcn5yLLQPTgvpspT19yiU=",
"last_login": null,
"is_superuser": false,
"username": "member",
"first_name": "",
"last_name": "",
"email": "",
"is_staff": false,
"is_active": true,
"date_joined": "2021-03-23T09:45:00.549553Z",
"groups": [],
"user_permissions": []
},
{
"id": 3,
"articles": [
"会组织要依法登记、依法运作,这是世界各国通行的做法。我国是法治国家,支持合法登记的社会组织依法运作、维护合法登记的社会组织正当权益,是我们的一贯方针,同时,打击整治非法社会组织也是我们的一贯方针。多年来,各地各部门对非法社会组织持续保持高压态势,非法社会组织活动明显减少,社会组织生态空间日渐清朗。但近年来受利益驱动,一些非法社会组织不断变换手法,以假乱真、招摇撞骗,一些地方非法社会组织又有所滋长。"
],
"password": "pbkdf2_sha256$216000$0h2Wxbeboony$xgp2pvwfBMpJ0KmhpxhTutwQFU/loC3tvtqGMSuCm2o=",
"last_login": null,
"is_superuser": false,
"username": "member1",
"first_name": "",
"last_name": "",
"email": "",
"is_staff": false,
"is_active": true,
"date_joined": "2021-03-23T09:45:18.034139Z",
"groups": [],
"user_permissions": []
}
]

使用关联对象的序列化器

查询所有用户及其发表的文章信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ArticleSerializer(serializers.ModelSerializer):
"""文章序列化类"""
full_status = serializers.ReadOnlyField(source="get_status_display")
author = serializers.ReadOnlyField(source="author.username")

class Meta:
model = Article
fields = "__all__"
read_only_fields = ('id', 'created_time', 'author')


class UserSerializer(serializers.ModelSerializer):
"""用户序列化类"""
articles = ArticleSerializer(many=True)

class Meta:
model = User
fields = "__all__"

输出结果:

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
[
{
"id": 1,
"articles": [
{
"id": 10,
"full_status": null,
"author": "admin",
"title": "fff少时诵诗书所所所所所所所所所所所所所所所所",
"body": "沙发撒的发是的发送到发生发",
"status": null,
"created_time": "2021-03-24T05:26:21.521804Z"
},
{
"id": 8,
"full_status": "草稿",
"author": "admin",
"title": "Django是如何写出来的",
"body": "发顺丰的",
"status": "d",
"created_time": "2021-03-24T02:32:03.208494Z"
},
{
"id": 6,
"full_status": "草稿",
"author": "admin",
"title": "我是一只小小鸟",
"body": "weasfasdf时发生地方",
"status": "d",
"created_time": "2021-03-24T02:21:26.062896Z"
}
],
"password": "pbkdf2_sha256$216000$H7uXzmEmLPFr$RaAYcUf6g8tgV9hLytiySyBp3Fo+LP52SZnCVfrYxTk=",
"last_login": "2021-03-23T09:19:42.312394Z",
"is_superuser": true,
"username": "admin",
"first_name": "",
"last_name": "",
"email": "admin@qq.com",
"is_staff": true,
"is_active": true,
"date_joined": "2021-03-23T09:18:58.092435Z",
"groups": [],
"user_permissions": []
},
{
"id": 2,
"articles": [
{
"id": 1,
"full_status": "发布",
"author": "member",
"title": "中方制裁名单上排第一的欧洲议员是谁?",
"body": "22日晚间,中国外交部宣布,决定对欧方严重损害中方主权和利益、恶意传播谎言和虚假信息的10名人员和4个实体实施制裁,以对欧盟以涉疆事务为由对中方个人和实体施加制裁进行反制。在外交部公布的制裁名单上,排在首位的是欧洲议会议员彼蒂科菲尔,他最近曾多次无端攻击中国的新疆政策,并以此为由鼓动对中国官员的制裁和阻挠中欧全面投资协定。",
"status": "p",
"created_time": "2021-03-23T09:46:50.668004Z"
}
],
"password": "pbkdf2_sha256$216000$rUpAo2698YTh$lM8HUQMlmknCF61aGtsmPWXcn5yLLQPTgvpspT19yiU=",
"last_login": null,
"is_superuser": false,
"username": "member",
"first_name": "",
"last_name": "",
"email": "",
"is_staff": false,
"is_active": true,
"date_joined": "2021-03-23T09:45:00.549553Z",
"groups": [],
"user_permissions": []
},
{
"id": 3,
"articles": [
{
"id": 2,
"full_status": "发布",
"author": "member1",
"title": "关于铲除非法社会组织滋生土壤 净化社会组织生态空间的通知",
"body": "会组织要依法登记、依法运作,这是世界各国通行的做法。我国是法治国家,支持合法登记的社会组织依法运作、维护合法登记的社会组织正当权益,是我们的一贯方针,同时,打击整治非法社会组织也是我们的一贯方针。多年来,各地各部门对非法社会组织持续保持高压态势,非法社会组织活动明显减少,社会组织生态空间日渐清朗。但近年来受利益驱动,一些非法社会组织不断变换手法,以假乱真、招摇撞骗,一些地方非法社会组织又有所滋长。",
"status": "p",
"created_time": "2021-03-23T09:47:43.792678Z"
}
],
"password": "pbkdf2_sha256$216000$0h2Wxbeboony$xgp2pvwfBMpJ0KmhpxhTutwQFU/loC3tvtqGMSuCm2o=",
"last_login": null,
"is_superuser": false,
"username": "member1",
"first_name": "",
"last_name": "",
"email": "",
"is_staff": false,
"is_active": true,
"date_joined": "2021-03-23T09:45:18.034139Z",
"groups": [],
"user_permissions": []
}
]

深度自动化连表

UserInfo类中有外键和多对多的键,直接序列化的结果是只显示id,如果想得到更多的信息可以用depth字段,可以理解为深度读取,默认为0,就是我们默认显示的结果。如果改为1,表示深度为1,就可以再往下读取一层所有的信息,在这里就可以读取到用户的角色和组别两个类中所有的信息。

查询所有文章及其发表的用户

1
2
3
4
5
6
7
8
class ArticleSerializer(serializers.ModelSerializer):
"""文章序列化类"""

class Meta:
model = Article
fields = "__all__"
read_only_fields = ('id', 'created_time', 'author')
depth = 1

输出结果:

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
[
{
"id": 10,
"title": "fff少时诵诗书所所所所所所所所所所所所所所所所",
"body": "沙发撒的发是的发送到发生发",
"status": null,
"created_time": "2021-03-24T05:26:21.521804Z",
"author": {
"id": 1,
"password": "pbkdf2_sha256$216000$H7uXzmEmLPFr$RaAYcUf6g8tgV9hLytiySyBp3Fo+LP52SZnCVfrYxTk=",
"last_login": "2021-03-23T09:19:42.312394Z",
"is_superuser": true,
"username": "admin",
"first_name": "",
"last_name": "",
"email": "admin@qq.com",
"is_staff": true,
"is_active": true,
"date_joined": "2021-03-23T09:18:58.092435Z",
"groups": [],
"user_permissions": []
}
},
{
"id": 8,
"title": "Django是如何写出来的",
"body": "发顺丰的",
"status": "d",
"created_time": "2021-03-24T02:32:03.208494Z",
"author": {
"id": 1,
"password": "pbkdf2_sha256$216000$H7uXzmEmLPFr$RaAYcUf6g8tgV9hLytiySyBp3Fo+LP52SZnCVfrYxTk=",
"last_login": "2021-03-23T09:19:42.312394Z",
"is_superuser": true,
"username": "admin",
"first_name": "",
"last_name": "",
"email": "admin@qq.com",
"is_staff": true,
"is_active": true,
"date_joined": "2021-03-23T09:18:58.092435Z",
"groups": [],
"user_permissions": []
}
},
{
"id": 6,
"title": "我是一只小小鸟",
"body": "weasfasdf时发生地方",
"status": "d",
"created_time": "2021-03-24T02:21:26.062896Z",
"author": {
"id": 1,
"password": "pbkdf2_sha256$216000$H7uXzmEmLPFr$RaAYcUf6g8tgV9hLytiySyBp3Fo+LP52SZnCVfrYxTk=",
"last_login": "2021-03-23T09:19:42.312394Z",
"is_superuser": true,
"username": "admin",
"first_name": "",
"last_name": "",
"email": "admin@qq.com",
"is_staff": true,
"is_active": true,
"date_joined": "2021-03-23T09:18:58.092435Z",
"groups": [],
"user_permissions": []
}
},
{
"id": 2,
"title": "关于铲除非法社会组织滋生土壤 净化社会组织生态空间的通知",
"body": "会组织要依法登记、依法运作,这是世界各国通行的做法。我国是法治国家,支持合法登记的社会组织依法运作、维护合法登记的社会组织正当权益,是我们的一贯方针,同时,打击整治非法社会组织也是我们的一贯方针。多年来,各地各部门对非法社会组织持续保持高压态势,非法社会组织活动明显减少,社会组织生态空间日渐清朗。但近年来受利益驱动,一些非法社会组织不断变换手法,以假乱真、招摇撞骗,一些地方非法社会组织又有所滋长。",
"status": "p",
"created_time": "2021-03-23T09:47:43.792678Z",
"author": {
"id": 3,
"password": "pbkdf2_sha256$216000$0h2Wxbeboony$xgp2pvwfBMpJ0KmhpxhTutwQFU/loC3tvtqGMSuCm2o=",
"last_login": null,
"is_superuser": false,
"username": "member1",
"first_name": "",
"last_name": "",
"email": "",
"is_staff": false,
"is_active": true,
"date_joined": "2021-03-23T09:45:18.034139Z",
"groups": [],
"user_permissions": []
}
},
{
"id": 1,
"title": "中方制裁名单上排第一的欧洲议员是谁?",
"body": "22日晚间,中国外交部宣布,决定对欧方严重损害中方主权和利益、恶意传播谎言和虚假信息的10名人员和4个实体实施制裁,以对欧盟以涉疆事务为由对中方个人和实体施加制裁进行反制。在外交部公布的制裁名单上,排在首位的是欧洲议会议员彼蒂科菲尔,他最近曾多次无端攻击中国的新疆政策,并以此为由鼓动对中国官员的制裁和阻挠中欧全面投资协定。",
"status": "p",
"created_time": "2021-03-23T09:46:50.668004Z",
"author": {
"id": 2,
"password": "pbkdf2_sha256$216000$rUpAo2698YTh$lM8HUQMlmknCF61aGtsmPWXcn5yLLQPTgvpspT19yiU=",
"last_login": null,
"is_superuser": false,
"username": "member",
"first_name": "",
"last_name": "",
"email": "",
"is_staff": false,
"is_active": true,
"date_joined": "2021-03-23T09:45:00.549553Z",
"groups": [],
"user_permissions": []
}
}
]

数据验证

在反序列化数据时,必需对用户提交的数据进行验证。在尝试访问经过验证的数据或保存对象实例之前,总是需要显示地调用 is_valid()方法。如果发生任何验证错误,.errors 属性将包含表示结果错误消息的字典,如下所示:

1
2
3
4
5
serializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'email': [u'Enter a valid e-mail address.'], 'created': [u'This field is required.']}

字典中的每个键都是字段名称,值是与该字段对应的任何错误消息的字符串列表。

.is_valid() 方法使用可选的 raise_exception 标志,如果存在验证错误,将会抛出 serializers.ValidationError 异常。

这些异常由 REST framework 提供的默认异常处理程序自动处理,默认情况下将返回 HTTP 400 Bad Request 响应。

1
2
# 如果数据未通过验证,返回400
serializer.is_valid(raise_exception=True)

在DRF中编写序列化类时,你可以进行字段级别和对象级别的验证,还可以使用自定义验证器或使用DRF提供的验证器。

字段级别验证 (Field-level validation)

您可以通过向您的 Serializer 子类中添加 .validate_<field_name> 方法来指定自定义字段级的验证。这些类似于 Django 表单中的 .clean_<field_name> 方法。这些方法采用单个参数,即需要验证的字段值。

您的 validate_<field_name> 方法应该返回已验证的值或抛出 serializers.ValidationError 异常。例如我们要验证文章标题不能包含CNM:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ArticleSerializer(serializers.ModelSerializer):
"""文章序列化类"""
full_status = serializers.ReadOnlyField(source="get_status_display")
# author = serializers.ReadOnlyField(source="author.username")

def validate_title(self, value):
# 标题包含CNM报错
if "CNM" in value:
raise serializers.ValidationError("禁止说脏话")
return value

class Meta:
model = Article
fields = "__all__"
read_only_fields = ('id', 'created_time', 'author')

我们提交标题中带有CNM的数据后台响应的结果为:

1
2
3
4
5
{
"title": [
"禁止说脏话"
]
}

对象级别验证 (Object-level validation)

要执行需要访问多个字段的任何其他验证,请添加名为 .validate() 的方法到您的 Serializer 子类中。此方法采用单个参数,该参数是字段值的字典。如果需要,它应该抛出 ValidationError 异常,或者只返回经过验证的值。

例如标题不能出现CNM并且内容也不能出现CNM:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ArticleSerializer(serializers.ModelSerializer):
"""文章序列化类"""
full_status = serializers.ReadOnlyField(source="get_status_display")
# author = serializers.ReadOnlyField(source="author.username")

def validate(self, data):
# 判断标题和内容中有脏字则返回错误信息
if "CNM" in data["title"] or "CNM" in data["body"]:
raise serializers.ValidationError("标题或内容中禁止有脏字")
return data

class Meta:
model = Article
fields = "__all__"
read_only_fields = ('id', 'created_time', 'author')

我们在标题或者内容中携带了CNM看下结果返回:

1
2
3
4
5
{
"non_field_errors": [
"标题或内容中禁止有脏字"
]
}

验证器 (Validators)

序列化器上的各个字段都可以包含验证器,通过在字段实例上声明.

例如还是刚才的问题,验证标题或者内容是否有脏字:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def validate_fuck(value):
# 判断标题和内容中有脏字则返回错误信息
if "CNM" in value:
raise serializers.ValidationError("禁止有脏字")


class ArticleSerializer(serializers.ModelSerializer):
"""文章序列化类"""
full_status = serializers.ReadOnlyField(source="get_status_display")
title = serializers.CharField(validators=[validate_fuck])
body = serializers.CharField(validators=[validate_fuck])


class Meta:
model = Article
fields = "__all__"
read_only_fields = ('id', 'created_time', 'author')

故意违规返回结果:

1
2
3
4
5
6
7
8
{
"title": [
"禁止有脏字"
],
"body": [
"禁止有脏字"
]
}

重写序列化器的create和update方法

假设我们有个Profile模型与User模型是一对一的关系,当用户注册时我们希望把用户提交的数据分别存入User和Profile模型,这时我们就不得不重写序列化器自带的create方法了。下例演示了如何通过一个序列化器创建两个模型对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class UserSerializer(serializers.ModelSerializer):
# 嵌套关联对象序列化器
profile = ProfileSerializer()

class Meta:
model = User
fields = ('username', 'email', 'profile')

def create(self, validated_data):
# 取出profile信息
profile_data = validated_data.pop('profile')
# 添加用户
user = User.objects.create(**validated_data)
# 添加用户对应的详情信息
Profile.objects.create(user=user, **profile_data)
return user

同时更新两个关联模型实例时也同样需要重写update方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def update(self, instance, validated_data):
# 取出前端提交的用户详情数据
profile_data = validated_data.pop('profile')
# 取出该用户对应的原有用户详情数据
profile = instance.profile
# 修改用户名和邮箱并保存
instance.username = validated_data.get('username', instance.username)
instance.email = validated_data.get('email', instance.email)
instance.save()
# 修改用户详情的姓名和地址,如果前端没有传递则使用原有姓名和地址,最后保存
profile.name= profile_data.get(
'name',
profile.name
)
profile.address = profile_data.get(
'address',
profile.address
)
profile.save()

# 返回修改的用户对象实例
return instance

因为序列化器使用嵌套后,创建和更新的行为可能不明确,并且可能需要相关模型之间的复杂依赖关系,REST framework要求你始终显式的编写这些方法。默认的 ModelSerializer .create().update() 方法不包括对可写嵌套表示的支持,所以我们总是需要对create和update方法进行重写。

动态加载序列化器类

有时你在类视里不希望通过通过serializer_class指定固定的序列化器类,而是希望动态的加载序列化器,你可以重写get_serializer_class方法,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class UserViewSet(CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,viewsets.GenericViewSet):

# 这个就不需要了
#serializer_class = XXXSerializer

def get_serializer_class(self):
# 如果是post创建则使用CustomSerializer1序列化器
if self.action == 'create':
return CustomSerializer1
# 如果是显示所有用户则使用XXXSerializer
elif self.action == 'list':
return XXXSerializer
# 如果是其他请求方式则使用CustomSerializer2
return CustomSerializer2

小结

  • 改变序列化输出数据的格式可以通过指定字段的source来源,使用SerializerMethodField自定义方法以及使用嵌套序列化器。
  • 反序列化时需要对客户端发送的数据进行验证。你可以通过自定义validate方法进行字段或对象级别的验证,你还可以使用自定义的validators或DRF自带的验证器。
  • 当你使用嵌套序列化器后,多个关联模型的创建和更新的行为并不明确,你需要显示地重写create和update方法。