Serializers
序列化程序允许将复杂数据(如 querysets 和 modle 实例)转换为原生的Python数据类型,然后可以将其简单地呈现为JSON,XML或其他内容类型。 序列化器还提供反序列化,允许解析的数据在首次验证传入数据后转换成复杂类型。
REST框架中的序列化工作与Django的Form和ModelForm类非常相似。 我们提供了一个Serializer类,它为您提供了强大的通用方法来控制响应的输出,以及一个ModelSerializer类,它为创建用于处理模型实例和查询结果的序列化程序提供了有用的快捷方式。
Declaring Serializers(声明序列化)
让我们从创建一个简单的对象开始,我们可以使用这个例子:
from datetime import datetime
class Comment(object):
def __init__(self, email, content, created=None):
self.email = email
self.content = content
self.created = created or datetime.now()
comment = Comment(email='leila@example.com', content='foo bar')
我们将声明一个序列化器,我们可以使用它来序列化和反序列化与Comment对象相对应的数据。
声明一个序列化器看起来非常类似于声明一个表单:
from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
Serializing objects
我们可以用CommentSerializer 来序列化一个comment或者comment的列表。再一次说,使用Serializer类和使用Form类很类似。
serializer = CommentSerializer(comment)
serializer.data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}
此时,我们将模型实例转换为Python原生数据类型。 要完成序列化过程,我们将数据呈现给json。
from rest_framework.renderers import JSONRenderer
json = JSONRenderer().render(serializer.data)
json
# b'{"email":"leila@example.com","content":"foo bar","created":"2016-01-27T15:17:10.375877"}'
Deserializing objects
反序列化也是类似的,首先我们将一个流解析为Python原生数据类型...
from django.utils.six import BytesIO
from rest_framework.parsers import JSONParser
stream = BytesIO(json)
data = JSONParser().parse(stream)
...然后我们将这些原生数据类型恢复到已验证数据的字典中。
serializer = CommentSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# {'content': 'foo bar', 'email': 'leila@example.com', 'created': datetime.datetime(2012, 08, 22, 16, 20, 09, 822243)}
Saving instances
如果我们希望能够根据验证的数据返回完整的对象实例,我们需要实现.create()和update()方法之一或两者。 例如:
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
def create(self, validated_data):
return Comment(**validated_data)
def update(self, instance, validated_data):
instance.email = validated_data.get('email', instance.email)
instance.content = validated_data.get('content', instance.content)
instance.created = validated_data.get('created', instance.created)
return instance
如果你的对象实例对应于Django模型,您还需要确保这些方法将对象保存到数据库。 例如,如果Comment是Django模型,那么这些方法可能如下所示:
def create(self, validated_data):
return Comment.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.email = validated_data.get('email', instance.email)
instance.content = validated_data.get('content', instance.content)
instance.created = validated_data.get('created', instance.created)
instance.save()
return instance
现在反序列化数据时,我们可以根据验证的数据调用.save()返回一个对象实例。
comment = serializer.save()
调用.save()将创建一个新的实例或更新一个现有的实例,具体取决于在实例化序列化器类时是否传递了一个现有的实例:
# .save() will create a new instance.
serializer = CommentSerializer(data=data)
# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)
.create()和.update()方法都是可选的。 你可以根据你的Serializer类的用例来实现它们之一,一个或两者。
Passing additional attributes to .save()
有时你会希望你的视图代码能够在保存实例时注入额外的数据。 此附加数据可能包括当前用户,当前时间或不是请求数据一部分的其他信息。
您可以通过在调用.save()时包含其他关键字参数来执行此操作。 例如:
serializer.save(owner=request.user)
当调用.create()或.update()时,任何其他关键字参数将被包含在validated_data参数中。
Overriding .save() directly.
在某些情况下,.create()和.update()方法名称可能无意义。 例如,在联系表单中,我们可能不会创建新的实例,而是发送电子邮件或其他消息。
在这些情况下,你可以直接选择覆盖.save(),因为它更可读和有意义。
例如:
class ContactForm(serializers.Serializer):
email = serializers.EmailField()
message = serializers.CharField()
def save(self):
email = self.validated_data['email']
message = self.validated_data['message']
send_email(from=email, message=message)
请注意,在上述情况下,我们现在必须直接访问serializer_date属性。
Validation
反序列化数据时,你始终需要在尝试访问经过验证的数据之前调用is_valid()或保存对象实例。 如果发生任何验证错误,则.errors属性将包含表示生成的错误消息的字典。 例如:
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.']}
字典中的每个键都将是字段名称,值将是与该字段对应的任何错误消息的字符串列表。 non_field_errors键也可能存在,并将列出任何一般验证错误。 可以使用NON_FIELD_ERRORS_KEY REST框架设置来定制non_field_errors键的名称。
当对项目列表进行反序列化时,将作为代表每个反序列化项目的字典列表返回错误。
Raising an exception on invalid data
.is_valid()方法接受一个可选的raise_exception标志,如果存在验证错误,它将引发一个serializers.ValidationError异常。
这些异常由REST框架提供的默认异常处理程序自动处理,默认情况下将返回HTTP 400错误请求响应。
# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)
Field-level validation
.is_valid()方法接受一个可选的raise_exception标志,如果验证错误,它将引发一个serializers.ValidationError异常。
这些异常由REST框架提供的默认异常处理程序自动处理,默认情况下将返回HTTP 400错误请求响应。
# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)
Field-level validation
您可以通过将.validate_ <field_name>方法添加到Serializer子类来指定自定义字段级验证。 这些类似于Django表单上的.clean_ <field_name>方法。
这些方法采用单个参数,即需要验证的字段值。
您的validate_ <field_name>方法应返回验证的值或提出serializers.ValidationError。 例如:
from rest_framework import serializers
class BlogPostSerializer(serializers.Serializer):
title = serializers.CharField(max_length=100)
content = serializers.CharField()
def validate_title(self, value):
"""
Check that the blog post is about Django.
"""
if 'django' not in value.lower():
raise serializers.ValidationError("Blog post is not about Django")
return value
注意:如果您的<field_name>在您的串行器上声明,参数required = False,则不会在不包含该字段的情况下执行此验证步骤。
Object-level validation
要执行需要访问多个字段的任何其他验证,请将一个名为.validate()的方法添加到Serializer子类。 该方法采用单个参数,它是字段值的字典。 如果需要,它应该引发ValidationError,或者只返回验证的值。 例如:
from rest_framework import serializers
class EventSerializer(serializers.Serializer):
description = serializers.CharField(max_length=100)
start = serializers.DateTimeField()
finish = serializers.DateTimeField()
def validate(self, data):
"""
Check that the start is before the stop.
"""
if data['start'] > data['finish']:
raise serializers.ValidationError("finish must occur after start")
return data
Validators
串行器上的各个字段可以包括验证器,通过在字段实例上声明它们,例如:
def multiple_of_ten(value):
if value % 10 != 0:
raise serializers.ValidationError('Not a multiple of ten')
class GameRecord(serializers.Serializer):
score = IntegerField(validators=[multiple_of_ten])
...
序列化器类还可以包括应用于整套现场数据的可重用验证器。 这些验证器包含在内部Meta类上声明它们,如下所示:
class EventSerializer(serializers.Serializer):
name = serializers.CharField()
room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
date = serializers.DateField()
class Meta:
# Each room only has one event per day.
validators = UniqueTogetherValidator(
queryset=Event.objects.all(),
fields=['room_number', 'date']
)
有关更多信息,请参阅验证器文档。
Accessing the initial data and instance
将初始对象或查询集传递给序列化器实例时,该对象将以.instance形式提供。 如果没有初始对象被传递,那么.instance属性将为None。
将数据传递给串行器实例时,未修改的数据将作为.initial_data提供。 如果data关键字参数未被传递,则.initial_data属性将不存在。
Partial updates
默认情况下,序列化程序必须传递所有必填字段的值,否则会引发验证错误。 您可以使用部分参数以允许部分更新。
# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)
Dealing with nested objects
以前的示例适用于处理只有简单数据类型的对象,但有时我们还需要能够表示更复杂的对象,其中对象的某些属性可能不是简单的数据类型,如字符串,日期或整数。
Serializer类本身就是一个Field类型,可以用来表示一个对象类型嵌套在另一个对象之间的关系。
class UserSerializer(serializers.Serializer):
email = serializers.EmailField()
username = serializers.CharField(max_length=100)
class CommentSerializer(serializers.Serializer):
user = UserSerializer()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
如果嵌套表示可以选择接受无值,则应将required = False标志传递给嵌套的序列化程序。
class CommentSerializer(serializers.Serializer):
user = UserSerializer(required=False) # May be an anonymous user.
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
类似地,如果嵌套表示应该是项目列表,则应该将许多= True标志传递给嵌套的序列化。
class CommentSerializer(serializers.Serializer):
user = UserSerializer(required=False)
edits = EditItemSerializer(many=True) # A nested list of 'edit' items.
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
Writable nested representations
当处理支持反序列化数据的嵌套表示时,嵌套对象的任何错误都嵌套在嵌套对象的字段名下。
serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'user': {'email': [u'Enter a valid e-mail address.']}, 'created': [u'This field is required.']}
类似地,.validated_data属性将包括嵌套的数据结构。
Writing .create() methods for nested representations
如果您支持可写嵌套表示,则需要编写处理保存多个对象的.create()或.update()方法。
以下示例演示如何处理创建具有嵌套概要文件对象的用户。
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ('username', 'email', 'profile')
def create(self, validated_data):
profile_data = validated_data.pop('profile')
user = User.objects.create(**validated_data)
Profile.objects.create(user=user, **profile_data)
return user