序列化:将复杂数据(如查询集和模型实例对象)转换为本机 Python 数据类型,然后可以轻松地转换为JSON
、xml
或者其他内容类型;
反序列化:将JSON
、xml
等转换回原来的python数据类型。
DRF序列化器的用法与Django Form表单的用法极为相似,它提供了Serializer
和ModelSerializer
类,帮助我们处理模型实例和查询集的序列化与反序列化,并提供了一些强大的功能。
我们通过实现“获取一本书的信息”这样一个需求,来学习序列化过程。在搭建好环境之后:
创建模型类:
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=30, verbose_name="书籍名称")
author = models.CharField(max_length=20, verbose_name="作者")
price = models.DecimalField(
max_digits=5, decimal_places=2, verbose_name="价格")
publisher = models.CharField(max_length=20, verbose_name="出版社")
class Meta:
verbose_name = "书籍"
verbose_name_plural = "书籍"
创建序列化类,继承serializers.Serializer
,与django Form表单的写法类似:
from rest_framework import serializers
class BookSerializer(serializers.Serializer):
title = serializers.CharField()
author = serializers.CharField()
price = serializers.DecimalField(max_digits=5, decimal_places=2)
publisher = serializers.CharField()
序列化类的字段与django的模型类字段基本对应,定义方法也基本一致。
编写视图,需要继承APIView
,返回DRF的Response(序列化对象.data)
对象或者返回django的JsonResponse(序列化对象.data)
对象:
from rest_framework.response import Response
from rest_framework.views import APIView
from quickstart.models import Book
from quickstart.serializers import BookSerializer
class BookView(APIView):
def get(self, request, id):
book = Book.objects.filter(id=id).first()
# 实例化序列化器类,传入需要序列化的对象
book_serializer = BookSerializer(book)
# book_serializer.data存放着序列化之后的数据
return Response(book_serializer.data)
添加路由:
from django.contrib import admin
from django.urls import include, path
from quickstart.views import BookView
urlpatterns = [
path('admin/', admin.site.urls),
path('book//' , BookView.as_view())
]
之后,我们随便添加几本书籍到数据库。然后访问路径book/1/
,就能看到id为1的书籍信息了(JSON格式)。
反序列化过程同样很简单,我们在上面代码的基础之上,通过实现“修改一本书的信息”这样一个需求,来学习反序列化过程。
在视图中,获取模型对象并生成序列化器对象:
class BookView(APIView):
def put(self, request, id):
response_content = {"code":200, "msg":"修改成功!"}
book = Book.objects.filter(id=id).first()
# 生成序列化器对象,传入模型对象和前端提交的数据即可
book_serializer = BookSerializer(book, request.data)
# 如果提交的数据通过校验,就进行保存
if book_serializer.is_valid():
book_serializer.save()
response_content["data"] = book_serializer.data
return Response(response_content)
else:
response_content["code"] = 400
response_content["msg"] = "修改失败!"
response_content["data"] = book_serializer.errors
return Response(response_content)
上面代码第10行中的sava()
方法,依赖于create()
和update()
方法中的一个或两个,但这两个方法被定义为抽象方法(因为对模型对象的创建和修改依赖于具体场景),所以需要我们要自己实现,否则会报错。
至于save()
具体会用到create()
还是update()
?取决于实例化序列器时是否传入了已有的模型实例。
由于我们在实例化序列化器时,传入了一个已存在的Book实例对象,所以save()
会用到update()
,我们就需要实现该方法:
class BookSerializer(serializers.Serializer):
……
def update(self, instance, validated_data):
# instance就是已存在的Book实例对象
instance.title = validated_data.get("title")
instance.author = validated_data.get("author")
instance.price = validated_data.get("price")
instance.publisher = validated_data.get("publisher")
# 由于实例对象是模型对象,所以需要调用模型对象的save方法保存到数据库
instance.save()
return instance
之后,我们就可以对书籍信息进行修改了。
序列化器的绝大部分字段与django的模型类字段是对应的,定义方法也基本一致,所以不再展开解释,详细的可以参考官方文档:
序列化器字段 | 字段构造方式 |
---|---|
BooleanField | BooleanField() |
NullBooleanField | NullBooleanField() |
CharField | CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True) |
EmailField | EmailField(max_length=None, min_length=None, allow_blank=False) |
RegexField | RegexField(regex, max_length=None, min_length=None, allow_blank=False) |
SlugField | SlugField(max_length=50, min_length=None, allow_blank=False) |
URLField | URLField(max_length=200, min_length=None, allow_blank=False) |
UUIDField | UUIDField(format=‘hex_verbose’) format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) |
IPAddressField | IPAddressField(protocol=‘both’, unpack_ipv4=False, **options) |
IntegerField | IntegerField(max_value=None, min_value=None) |
FloatField | FloatField(max_value=None, min_value=None) |
DecimalField | DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) |
DateTimeField | DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None) |
DateField | DateField(format=api_settings.DATE_FORMAT, input_formats=None) |
TimeField | TimeField(format=api_settings.TIME_FORMAT, input_formats=None) |
DurationField | DurationField() |
ChoiceField | ChoiceField(choices) |
MultipleChoiceField | MultipleChoiceField(choices) |
FileField | FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ImageField | ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
ListField | ListField(child=, min_length=None, max_length=None) |
DictField | DictField(child) |
参数名称 | 作用 |
---|---|
max_length | 最大长度 |
min_lenght | 最小长度 |
allow_blank | 是否允许为空 |
trim_whitespace | 是否截断空白字符 |
max_value | 最小值 |
min_value | 最大值 |
下面这些参数时各字段通用的:
read_only:
表示该字段是只读的,只在序列化时使用该字段,而在反序列化期间创建或更新实例时不使用该字段。假如在反序列化时,传入了该字段,则会被忽略。
默认为False
write_only:
表示该字段是只写的,与read_only恰好相反,表示只在反序列化期间创建或更新实例时使用该字段,序列化期间不使用。
默认为False
read_only
与write_only
参数在某些情况下很有用。比如,有时候我们进行反序列化时,某些字段是不需要前端提交的,我们就可以将其设置为read_only
,允许前端不提交该字段的值。
required:
如果反序列化期间未提供字段,通常会引发错误;如果反序列化过程中不需要此字段,则设置为false。
默认为True
allow_null:
表明是否允许传入None。
默认为False
default:
反序列化时使用的默认值。
注意:设置default值意味着该字段不是必需的。同时包含default和required关键字参数都是无效的,并且会引发错误。
source:
该参数接受一个模型类属性,该属性会被用于生成序列化器类的字段。
该参数的默认值为字段的名称。所以,当我们没有传入该参数,并且字段名称与模型类的所有属性名称都不匹配时,就会报错。
class BookSerializer(serializers.Serializer):
"""
模型类中的属性名称为 title,所以下面的代码会报错
book_name = serializers.CharField()
"""
# 这里通过source指定了正确的属性名称,所以不会报错
book_name = serializers.CharField(source="title")
它也可以传入只接受一个self
参数的方法,该方法定义在模型类中,其返回值会被传给该字段。例如:
url = URLField(source='get_absolute_url')
还可以跨表查询,比如通过图书查询作者,再通过作者查询作者邮箱:
email = EmailField(source='author.email')
另外,source='*'
有特殊含义,代表将整个对象传递给该字段。这对于创建嵌套表示或对于需要访问完整对象才能确定输出表示的字段很有用。
validators:
验证器函数列表,应将其应用于输入字段。
验证器函数应该引发验证错误或简单地返回。
error_messages:
包含错误编号与错误信息的字典。
label:
用于HTML展示API页面时,显示的字段名称。
help_text:
用于HTML展示API页面时,显示的字段帮助提示信息。
更多字段和参数请参考官方文档:传送门
使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。
在获取反序列化的数据前,必须调用is_valid()
方法进行验证,验证成功返回True,否则返回False。
validated_data
属性获取数据**;errors
属性获取错误信息**,返回字典,包含了字段和字段的错误。如果是非字段错误,可以通过修改REST framework配置中的NON_FIELD_ERRORS_KEY
来控制错误字典中的键名。is_valid()
方法还可以在验证失败时抛出serializers.ValidationError
异常,可以通过传递raise_exception=True
参数开启,REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应。
serializer.is_valid(raise_exception=True)
DRF自带的校验方法不够强大,但它允许我们通过以下三种方法自定义校验方法:
如果只对单个字段进行校验,我们可以通过向序列化器类中添加validate_字段名称
方法实现自定义字段级别的验证。这些类似于Django表单中的clean_字段名称
方法:
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
class BookSerializer(serializers.Serializer):
……
def validate_price(self, value):
"""局部校验price字段,参数value就是price字段的值"""
# 数据不合乎要求就抛出ValidationError异常
if float(value) < 10:
raise ValidationError("价格过低,不能低于10元")
# 符合要求就直接返回
else:
return value
如果要同时对多个字段进行校验,可以定义validate方法来验证:
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
class BookSerializer(serializers.Serializer):
……
def validate(self, attrs):
if attrs["author"] == attrs["publisher"]:
raise ValidationError("作者姓名与出版社名称相同!")
else:
return attrs
在字段中添加validators选项参数,指定验证器函数(可以有多个),就会用验证器函数校验该字段。
def about_django(value):
if 'django' not in value.lower():
raise serializers.ValidationError("图书不是关于Django的")
class BookSerializer(serializers.Serializer):
"""图书数据序列化器"""
btitle = serializers.CharField(label='名称', max_length=20, validators=[about_django]) # 注意:validators的实参是列表
……
我们在上面序列化过程中所展示的就是查询单条数据的过程。
与查询单条数据不同,如果要序列化多个实例,则需要在实例化序列化器时,传入many=True
参数。
class BooksView(APIView):
def get(self, request):
"""返回所有书籍"""
books = Book.objects.all()
books_serializer = BookSerializer(books, many=True)
response_content = {"code": 200, "msg": "获取成功!",
"data": books_serializer.data}
return Response(response_content)
class BookView(APIView):
……
def post(self, request):
"""新增一本书籍"""
# 传入request.data,对前端数据提交的数据反序列化
book_serializer = BookSerializer(data=request.data)
# 务必要先验证,切记!!!
if book_serializer.is_valid():
book_serializer.save()
resonse_content = {"code": 200,"data":book_serializer.data}
else:
resonse_content = {"code": 400, "data": book_serializer.errors}
return Response(resonse_content)
调用了序列化器的save方法,就要实现create()
方法,切记!!!
class BookSerializer(serializers.Serializer):
……
def create(self, validated_data):
# validated_data就是经过验证的前端提交的数据
return Book.objects.create(**validated_data) # 注意拆包
修改的过程,就是上面的反序列化的过程。与新增不同的地方在于实例化序列化器时,多传入了一个已存在的模型实例对象,需要我们手动实现的是update()
方法,而非create()
方法。
一般情况下,不会用到序列化器,通过模型类直接删除即可。
class BookView(APIView):
……
def delete(self, request, id):
Book.objects.filter(id=id).delete()
return Response({"code":200, "msg":"删除成功!"})
之前,我们的Serializer
类继承了serializer.Serializer
,需要手动一个个写入字段,较为麻烦。所以,DRF我们提供了ModelSerializer
类来帮助我们快速创建一个Serializer
类。
继承了ModelSerializer
类的Serializer
类有以下特点:
Serializer
生成验证器,比如unique_together
;create()
和update()
的实现,不用我们去实现。class BookSerializer(serializers.ModelSerializer):
# 不需要编写字段
class Meta:
# 指定模型类
model = Book # Book模型类需要导入
# 指定序列化器包含模型类的哪些字段,这里为全部字段
fields="__all__" # 前后双下划线
模型类序列化器虽然不需要编写字段,但如果需要,还是允许我们编写一些额外的字段或者覆盖自动默认的字段。
使用fields
指定Serializer
类包含模型类的哪些字段,除了__all__
表名包含所有字段,还可以指定部分字段。
比如:fields = ('id', 'title', 'price')
另外,fields
中可以写模型类的方法名(包括自定义的),序列化器会自动调用该方法,将方法名作为键,返回值作为值。
使用exclude
可以明确排除掉哪些字段。
比如:exclude = ('image',)
通过read_only_fields
指明只读字段,即仅用于序列化输出的字段。
比如:read_only_fields = ('id', 'bread', 'bcomment')
write_only_fields
已被extra_kwargs
所替代。
使用extra_kwargs
参数为ModelSerializer
添加或修改原有的选项参数。比如:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ('id', 'title', 'price', 'author',)
extra_kwargs = {
'price': {'min_value': 10, 'required': True},
'author': {'write_only': True},
}
这是一个只读字段。接受一个方法,并将该方法的返回值添加到对象的序列化表示中。比如:
class BookSerializer(serializers.Serializer):
……
def new_author(self, instance):
return "花城最帅了"
author = serializers.SerializerMethodField("new_author")
在此之后,所有序列化输出中的author对应的值,就都成了“花城最帅了”。
值得注意的是,如果在实例化SerializerMethodField
类时不指定方法名称,则默认调用名称为get_
的方法。也就是说,上面代码中的new_author
得修改为get_author
。