序列化器允许诸如查询集和模型实例复杂的数据转换为原生的Python数据类型,然后可以很容易地呈现为JSON
,XML
或其他内容类型。序列化器还提供反序列化功能,允许在首先验证输入数据之后将解析的数据转换回复杂类型。
REST框架中的序列化器的工作方式与Django Form
和ModelForm
类非常相似。我们提供了一个Serializer
类,该类为您提供了一种强大的通用方法来控制响应的输出,还ModelSerializer
提供了一个类,该类为创建用于处理模型实例和查询集的序列化器提供了有用的快捷方式。
让我们从创建一个简单的对象开始,我们可以将其用于示例目的:
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
对象相对应的数据。
声明序列化器看起来与声明表单Form非常相似:
from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
现在,我们可以使用CommentSerializer
序列化comment对象或多个comment的列表。同样,使用Serializer
类看起来很像使用Form
类。
serializer = CommentSerializer(comment)
serializer.data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}
type(serializer.data)
# <class 'rest_framework.utils.serializer_helpers.ReturnDict'>
至此,我们已经将一个comment实例转换为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"}'
type(json)
# <class 'bytes'>
反序列化是相似的。首先,我们将流解析为Python原生数据类型
import io
from rest_framework.parsers import JSONParser
stream = io.BytesIO(json)
# <_io.BytesIO object at 0x000001BAD7B29678>
data = JSONParser().parse(stream)
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2019-12-19T10:15:08.513248Z'}
type(data)
# <class 'dict'>
然后我们将这些本机数据类型还原为经过验证的数据字典
注意:依然是调用CommentSerializer类的构造函数,但是给data参数传递数据,而不是第一位置参数,这表示反序列化过程。其次,数据有一个验证过程is_valid()
。上面给第一位置参数传参,表示的是序列化
serializer = CommentSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# OrderedDict([('email', 'leila@example.com'), ('content', 'foo bar'), ('created', datetime.datetime(2019, 12, 19, 10, 15, 8, 513248, tzi
nfo=<UTC>))])
type(serializer.validated_data)
# <class 'collections.OrderedDict'>
这个步骤做完,只是从json变成了原生的Python数据类型,还不是前面自定义的Comment类的对象
如果我们希望能够基于经过验证的数据返回完整的对象实例,则需要实现.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
上面的两个方法在其继承关系中的父类里定义了具体的参数形式,instance
和validated_data
都是由继承体系里定义了的,分别表示要返回的Comment实例对象和经过验证的数据。
如果您的对象实例与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
相比普通的Python的Comment类,对Django模型的保存多了一些ORM操作,比如save()方法。
现在,在对数据进行反序列化时,我们可以.save()
基于已验证的数据调用以返回对象实例。
comment = serializer.save()
调用.save()
将创建一个新实例,或更新一个现有实例,具体取决于在实例化序列化程序类时是否传递了一个现有实例:
# .save()将创建一个新的实例
serializer = CommentSerializer(data=data)
# .save() 将更新现有的' comment '实例
serializer = CommentSerializer(comment, data=data)
无论是.create()
和.update()
方法是可选的。根据序列化程序类的用例,您既可以实现它们之一,也可以两者都不实现。
有时,您希望视图代码能够在保存实例时注入其他数据。这些附加数据可能包括诸如当前用户,当前时间或不属于请求数据一部分的任何其他信息。
您可以通过在调用时包含其他关键字参数来实现.save()
。例如:
serializer.save(owner=request.user)
在.create()
或.update()
被调用时,所有其他的关键字参数将被包含在validated_data
参数中。
在某些情况下,.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的.validated_data
属性,因为没有写create或者update方法,没人给你传递validated_data
参数,只能从self里面自己取。
反序列化数据时,您始终需要先调用,is_valid()
然后再尝试访问经过验证的数据或保存对象实例。如果发生任何验证错误,则该.errors
属性将以字典的格式,代表产生的错误消息。例如:
serializer = CommentSerializer(data={'email': 'foobar', 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'email': ['Enter a valid e-mail address.'], 'created': ['This field is required.']}
词典中的每个键都是字段名称,值将是与该字段相对应的任何错误消息的字符串列表。该non_field_errors
键也可能存在,并将列出所有常规验证错误。non_field_errors
键的名称可以通过REST framework设置中的NON_FIELD_ERRORS_KEY
来自定义。
对项目列表进行反序列化时,将以代表每个反序列化项目的字典列表的形式返回错误。
is_valid()
方法带有一个raise_exception
异常标志,如果存在验证错误将导致抛出异常serializers.ValidationError
。
这些异常由REST framework提供的默认异常处理程序自动处理,并且将HTTP 400 Bad Request
默认返回响应。
# 如果数据无效,则返回400响应.
serializer.is_valid(raise_exception=True)
你可以通过向你的Serializer
子类中添加.validate_<field_name>
方法来指定自定义字段级别的验证。类似于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
参数,并且未给该字段提供参数,那么这个验证步骤不会执行。
要执行需要访问多个字段的任何其他验证,请添加一个.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 start is before finish.
"""
if data['start'] > data['finish']:
raise serializers.ValidationError("finish must occur after start")
return data
还可以通过在字段上声明验证器参数的方式为字段设置指定的验证器,例如:
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']
)
有关更多信息,请参见原文验证程序文档。
将初始化对象或者查询集传递给序列化实例时,可以通过.instance
访问原始对象。如果没有传递初始化对象,那么.instance
属性值将是None
。(正向,即序列化方向)
将数据传递给序列化器实例时,未修改的数据可以通过.initial_data
获取。如果没有传递data关键字参数,那么.initial_data
属性不存在。(反向,即反序列化方向
默认情况下,序列化器必须传递所有必填字段的值,否则就会引发验证错误。但是我们可以将 partial
参数指定为True,来允许部分更新
# 用部分数据更新“comment”
serializer = CommentSerializer(comment, data={'content': 'foo bar'}, partial=True)
前面的实例适用于处理只有简单数据类型的对象,但是有时候我们也需要表示更复杂的对象,其中对象的某些属性可能不是字符串、日期、整数这样简单的数据类型。
Serializer
类本身也是一种Field
,并且可以用来表示一个对象嵌套在另一个对象中的关系。也就是处理Django模型中的关系类型,一对一、一对多、多对多的字段。
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()
如果嵌套表示形式可以选择接受None
值,则应将required=False
标志传递给嵌套序列化器。
class CommentSerializer(serializers.Serializer):
user = UserSerializer(required=False) # May be an anonymous user.
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
类似的,如果嵌套的关联字段可以接收一个列表,那么应该将many=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()
当我们在处理包含关系字段的序列化过程中,如果关联字段的值不合法,同样会被检测出来,并且将错误信息保存在相应的位置,如下例,CommentSerializer中的user字段关联到auth的User表,但是提供了一个不合法的邮箱地址,sorry,你不能成功反序列化,DRF给出了明确的错误信息。类似的,.validated_data
属性也将包括关系字段的数据结构。
serializer = CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})
serializer.is_valid()
# False
serializer.errors
# {'user': {'email': ['Enter a valid e-mail address.']}, 'created': ['This field is required.']}
.create()
方法对于关系型字段,我们需要编写.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
.update()
方法对于更新,你需要仔细考虑如何处理关联字段的更新。 例如,如果关联字段的值是None
,或者没有提供,那么会发生下面哪种情况?
NULL
。下面是我们之前UserSerializer
类中update()
方法的一个例子。
def update(self, instance, validated_data):
profile_data = validated_data.pop('profile')
# Unless the application properly enforces that this field is
# always set, the follow could raise a `DoesNotExist`, which
# would need to be handled.
profile = instance.profile
instance.username = validated_data.get('username', instance.username)
instance.email = validated_data.get('email', instance.email)
instance.save()
profile.is_premium_member = profile_data.get(
'is_premium_member',
profile.is_premium_member
)
profile.has_support_contract = profile_data.get(
'has_support_contract',
profile.has_support_contract
)
profile.save()
return instance
因为关系字段的创建和更新行为可能不明确,并且可能需要关联模型间的复杂依赖关系,REST framework从3.x版本后要求你始终明确的定义这些方法。并且,默认的ModelSerializer
类的.create()
和.update()
方法不包括对关联字段的支持,需要你自己实现,或者需求第三方模块的支持。
但是,有第三方软件包,例如DRF Writable Nested,它们支持自动可写嵌套表示。
在序列化器中保存多个相关实例的另一种方法是编写处理创建正确实例的自定义模型管理器类,也就是Django原生的models.Manager
。
例如,假设我们想确保User
实例和Profile
实例总是作为一对一起创建。我们可能会写一个类似这样的自定义管理器类:
class UserManager(models.Manager):
...
def create(self, username, email, is_premium_member=False, has_support_contract=False):
user = User(username=username, email=email)
user.save()
profile = Profile(
user=user,
is_premium_member=is_premium_member,
has_support_contract=has_support_contract
)
profile.save()
return user
现在,该管理器类使得用户实例和用户信息实例总是在同一时间创建。我们在序列化器类上的.create()
方法现在可以用新的管理器方法重写一下。
def create(self, validated_data):
return User.objects.create(
username=validated_data['username'],
email=validated_data['email']
is_premium_member=validated_data['profile']['is_premium_member']
has_support_contract=validated_data['profile']['has_support_contract']
)
有关此方法的更多详细信息,请参阅有关模型管理器的Django文档,以及有关使用模型和管理器类的博客。
Serializer
类还可以序列化或反序列化对象的列表。
为了能够序列化一个查询集或者一个对象列表而不是一个单独的对象,需要在实例化序列化器类的时候传一个many=True
参数。这样就能序列化一个查询集或一个对象列表。
queryset = Book.objects.all()
serializer = BookSerializer(queryset, many=True)
serializer.data
# [
# {'id': 0, 'title': 'The electric kool-aid acid test', 'author': 'Tom Wolfe'},
# {'id': 1, 'title': 'If this is a man', 'author': 'Primo Levi'},
# {'id': 2, 'title': 'The wind-up bird chronicle', 'author': 'Haruki Murakami'}
# ]
反序列化多个对象的默认行为是支持多个对象创建,但不支持多个对象更新。有关如何支持或自定义这两种情况的更多信息,请参见下面的ListSerializer文档。
在某些情况下,除了要序列化的对象之外,还需要为序列化程序提供额外的上下文。一个常见的情况是,如果你使用包含超链接关系的序列化程序,这需要序列化器能够访问当前的请求以便正确生成完全限定的URL。
可以在实例化序列化器的时候传递一个context
参数来传递任意的附加上下文。例如:
serializer = AccountSerializer(account, context={'request': request})
serializer.data
# {'id': 6, 'owner': 'denvercoder9', 'created': datetime.datetime(2013, 2, 12, 09, 44, 56, 678870), 'details': 'http://example.com/accounts/6/details'}
.to_representation()
通过访问self.context
属性,可以在任何序列化程序字段逻辑(例如自定义方法)中使用上下文字典。
通常,您会需要与Django模型定义紧密映射的序列化程序类。
本ModelSerializer
类提供了一个快捷方式,可以让你自动创建一个具有模型中相应字段的Serializer
类。
ModelSerializer
类直接继承了Serializer
类,不同之处在于:
.create()
和.update()
。声明ModelSerializer
类的方法
如下所示:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
默认情况下,该类上的所有模型字段都将映射到相应的序列化器字段。
任何关系(例如模型上的外键)都将映射到PrimaryKeyRelatedField
。默认情况下不包括反向关系,除非按照序列化器关系文档中的指定明确包含反向关系。
如果你想确定ModelSerializer自动创建了哪些字段和验证器,可以打开Django shell。
为此,请使用打开Django shell,python manage.py shell
然后导入序列化程序类,实例化它,并打印对象表示形式…
>>> from myapp.serializers import AccountSerializer
>>> serializer = AccountSerializer()
>>> print(repr(serializer))
AccountSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(allow_blank=True, max_length=100, required=False)
owner = PrimaryKeyRelatedField(queryset=User.objects.all())
如果你希望在模型序列化器中只使用默认字段的一部分,你可以使用fields
或exclude
选项来执行此操作,就像使用ModelForm
一样。强烈建议你使用fields
属性显式的设置要序列化的字段。这样就不太可能因为你修改了模型而无意中暴露了数据。
例如:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
您也可以将fields
属性设置为特殊值,'__all__'
以指示应使用模型中的所有字段。
例如:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = '__all__'
您可以将exclude
属性设置为要从序列化器中排除的字段列表。
例如:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
exclude = ['users']
在上面的例子中,如果Account
模型具有3个字段account_name
,users
和created
,这将只有字段account_name
和created
被序列化。
fields
和exclude
属性中的名称通常会映射到模型类上的模型字段。
另外,fields
选项中的名称可以映射到属性或方法,这些属性或方法不包含模型类上存在的参数。
从3.3.0版开始,必须提供属性fields
或exclude
。
ModelSerializer
默认使用主键进行对象关联,但是你也可以使用depth
选项轻松生成嵌套关联:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
depth = 1
depth
选项应该设置一个整数值,表明应该遍历的关联深度。
如果要自定义序列化的方式你需要自定义该字段。
觉得全自动的字段不满足需求,也可以轻度定制,这就向基础的Serializer类靠拢了一点。
可以通过在ModelSerializer
类上显式声明字段来增加额外的字段或者重写默认的字段,就和在Serializer
类一样的。
class AccountSerializer(serializers.ModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True)
groups = serializers.PrimaryKeyRelatedField(many=True)
class Meta:
model = Account
您可能希望将多个字段指定为只读。除了使用read_only=True
属性显式添加每个字段外,您还可以使用快捷方式Meta选项read_only_fields
。
此选项应该是字段名称的列表或元组,并且声明如下:
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
read_only_fields = ['account_name']
模型中设置editable=False
的字段和AutoField
字段默认被设置为只读属性,不需要额外添加到read_only_fields
选项中。
注意: 有一种特殊情况,其中一个只读字段是模型级别unique_together
约束的一部分。在这种情况下,序列化器需要该字段的值才能验证约束,但也是不能由用户编辑的。
处理此问题的正确方法是在序列化器上显式指定该字段,同时提供read_only=True
和default=…
关键字参数。
这种情况的一个例子就是对于一个和其他标识符unique_together
的当前认证的User
是只读的。 在这种情况下你可以像下面这样声明user字段:
user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
请查看验证程序文档,以获取有关UniqueTogetherValidator和CurrentUserDefault类的详细信息。
还可以通过使用extra_kwargs
选项快捷地在字段上指定任意附加的关键字参数。这个选项是一个将具体字段名称当作键值的字典。例如:
class CreateUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['email', 'username', 'password']
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User(
email=validated_data['email'],
username=validated_data['username']
)
user.set_password(validated_data['password'])
user.save()
return user
请记住,如果已在序列化程序类上显式声明了该字段,则该extra_kwargs
选项将被忽略。
在序列化模型实例的时候,你可以选择多种不同的方式来表示关联关系。对于ModelSerializer
默认是使用相关实例的主键,也就是PrimaryKeyRelatedField。
替代的其他方法包括使用超链接序列化、序列化完整的嵌套表示或者使用自定义表示的序列化。
更多详细信息请查阅序列化器关系章节
ModelSerializer类还暴露了一个可以覆盖的API,以便在实例化序列化器时改变序列化器字段的自动确定方式。
通常情况下,如果ModelSerializer
没有生成默认情况下你需要的字段,那么你应该将它们显式地添加到类中,或者直接使用常规的Serializer
类。但是在某些情况下,你可能需要创建一个新的基类,定义任意模型创建序列化字段的方式。
.serializer_field_mapping
将Django model的字段类型映射到REST framework serializer的字段类型。你可以覆写这个映射。
.serializer_related_field
用于关联对象的字段类型。
对于ModelSerializer
此属性默认为PrimaryKeyRelatedField
。
对于HyperlinkedModelSerializer
此属性默认为serializers.HyperlinkedRelatedField
。
serializer_url_field
url
类型的字段类。默认是 serializers.HyperlinkedIdentityField。
serializer_choice_field
选择类型的字段类。默认是serializers.ChoiceField。
调用下面的方法来确定应该自动包含在序列化器类中每个字段的类和关键字参数。这些方法都应该返回 (field_class, field_kwargs)
元组。
.build_standard_field(self, field_name, model_field)
调用以生成映射到标准模型字段的序列化器字段。
默认实现基于serializer_field_mapping
属性返回一个序列化器类。
.build_relational_field(self, field_name, relation_info)
调用以生成映射到关系模型字段的序列化器字段。
默认实现基于serializer_related_field
属性返回一个序列化器类。
该relation_info
参数是一个命名元组,包含model_field
,related_model
,to_many
和has_through_model
属性。
.build_nested_field(self, field_name, relation_info, nested_depth)
当depth
选项被设置时,被调用后生成一个对应到关联模型字段的序列化器字段。。
默认实现基于ModelSerializer
或动态创建关系的序列化器类HyperlinkedModelSerializer
。
该nested_depthde
值是depth
的值减
一。
该relation_info
参数是一个命名元组,包含model_field
,related_model
,to_many
和has_through_model
属性。
.build_property_field(self, field_name, model_class)
被调用后生成一个对应到模型类中属性或无参数方法的序列化器字段。
默认实现返回一个ReadOnlyField
类。
.build_url_field(self, field_name, model_class)
被调用后为序列化器自己的url
字段生成一个序列化器字段。默认实现是返回一个HyperlinkedIdentityField
类。
.build_unknown_field(self, field_name, model_class)
当字段名称未映射到任何模型字段或模型属性时调用。尽管子类可以自定义此行为,但默认实现会引发错误。
HyperlinkedModelSerializer
类直接继承ModelSerializer
类,不同之处在于它使用超链接来表示关联关系而不是主键。
默认情况下,HyperlinkedModelSerializer序列化器将包含一个url
字段而不是主键字段。
url字段将使用HyperlinkedIdentityField
字段来表示,模型的任何关联都将使用HyperlinkedRelatedField
字段来表示。
你可以通过将主键添加到fields
选项中来显式的包含主键字段,例如下面的id
:
class AccountSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Account
fields = ['url', 'id', 'account_name', 'users', 'created']
这个类的源代码非常简单:
class HyperlinkedModelSerializer(ModelSerializer):
# 下面这句是核心
serializer_related_field = HyperlinkedRelatedField
def get_default_field_names(self, declared_fields, model_info):
# 覆写了ModelSerializer中的方法,在第一个变量处发生了变化,使用了url名字。
return (
[self.url_field_name] +
list(declared_fields) +
list(model_info.fields) +
list(model_info.forward_relations)
)
def build_nested_field(self, field_name, relation_info, nested_depth):
# 覆写了ModelSerializer中的方法,嵌套的子类依然继承的是HyperlinkedModelSerializer
class NestedSerializer(HyperlinkedModelSerializer):
class Meta:
model = relation_info.related_model
depth = nested_depth - 1
fields = '__all__'
field_class = NestedSerializer
field_kwargs = get_nested_relation_kwargs(relation_info)
return field_class, field_kwargs
当实例化一个HyperlinkedModelSerializer
时,你必须在序列化器的上下文中包含当前的request
值,例如:
serializer = AccountSerializer(queryset, context={'request': request})
这样做将确保超链接可以包含适当的主机名,以使生成的表示形式使用完全限定的URL,例如:
http://api.example.com/accounts/1/
而不是相对URL,例如:
/accounts/1/
如果你真的要使用相对URL,你应该明确的在序列化器上下文中传递一个{'request': None}
参数,而不是忽略不写。
我们为什么要使用超链接的序列化器?因为默认的主键字段只是冰冷的数字id,也就是1,2,3等等,在前端你无法知道它具体的含义。使用超链接则返回的是对应对象的url访问地址,是可以点击跳转直达的,更加形象。
既然是点击跳转可以直达,那么就需要确定哪些视图能应用超链接到模型实例的方法,否则没有对应的视图来处理这些链接的请求,就会404了。
默认情况下,超链接期望对应到一个样式能匹配'{model_name}-detail'
的视图,并通过pk
关键字参数查找实例。
那么可以通过在extra_kwargs
中设置view_name
和lookup_field
中的一个或两个来重写URL字段视图名称和查询字段。如下所示:
class AccountSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Account
fields = ['account_url', 'account_name', 'users', 'created']
extra_kwargs = {
'url': {'view_name': 'accounts', 'lookup_field': 'account_name'},
'users': {'lookup_field': 'username'}
}
或者你可以显式的设置序列化器上的字段。例如:
class AccountSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(
view_name='accounts',
lookup_field='slug'
)
users = serializers.HyperlinkedRelatedField(
view_name='user-detail',
lookup_field='username',
many=True,
read_only=True
)
class Meta:
model = Account
fields = ['url', 'account_name', 'users', 'created']
提示:正确匹配超链接表示和你的URL配置有时可能会有点困难。打印一个HyperlinkedModelSerializer
实例的repr
是一个特别有用的方式来检查关联关系映射的那些视图名称和查询字段。
URL字段的名称默认为'url'
。你可以在settings.py中修改URL_FIELD_NAME
配置项进行全局性修改。
ListSerializer继承的是BaseSerializer,属于Serializer的兄弟
ListSerializer
类能够一次性序列化和验证多个对象。我们通常不要直接使用ListSerializer
,而是应该在实例化一个序列化器时简单地传递一个many=True
参数。
当一个序列化器在带有many=True
选项被序列化时,实际将创建一个ListSerializer
类的实例。该序列化器类将成为ListSerializer
类的子类。
可以传递一个allow_empty
参数给ListSerializer
序列化器。这个参数的默认值是True
,但是如果你不想把空列表当作有效输入的话可以把它设置成False
。
ListSerializer
行为碰到下面的情况,你可能需要自定制ListSerializer
的一些行为:
对于这些情况,当你可以通过使用序列化器类的Meta
类下面的list_serializer_class
选项来修改当many=True
时正在使用的类。
也就是说这个时候你必须写一个继承了serializers.ListSerializer类的子类,然后在需要用它的序列化器类的Meta中添加list_serializer_class
条目。
例如:
class CustomListSerializer(serializers.ListSerializer):
...
class CustomSerializer(serializers.Serializer):
...
class Meta:
list_serializer_class = CustomListSerializer
默认情况下,多个对象的创建是简单的对列表中每个对象调用.create()
方法。如果要自定义实现,那么你需要自定义当被传递many=True
参数时使用的ListSerializer
类中的.create()
方法。
例如:
class BookListSerializer(serializers.ListSerializer):
def create(self, validated_data):
books = [Book(**item) for item in validated_data]
return Book.objects.bulk_create(books)
class BookSerializer(serializers.Serializer):
...
class Meta:
list_serializer_class = BookListSerializer
默认情况下,ListSerializer
该类不支持多个更新。这是因为插入和删除应预期的行为是模棱两可的。
要支持多个更新,您需要明确地这样做。在编写多个更新代码时,请确保牢记以下几点:
你需要向实例序列化器中显式添加一个id
字段。默认隐式生成的id
字段是read_only
。这就导致它在更新时被删除。一旦你明确地声明它,它将在列表序列化器的update
方法中可用。
下面是一个多对象更新的示例:
class BookListSerializer(serializers.ListSerializer):
def update(self, instance, validated_data):
# Maps for id->instance and id->data item.
book_mapping = {book.id: book for book in instance}
data_mapping = {item['id']: item for item in validated_data}
# Perform creations and updates.
ret = []
for book_id, data in data_mapping.items():
book = book_mapping.get(book_id, None)
if book is None:
ret.append(self.child.create(data))
else:
ret.append(self.child.update(book, data))
# Perform deletions.
for book_id, book in book_mapping.items():
if book_id not in data_mapping:
book.delete()
return ret
class BookSerializer(serializers.Serializer):
# We need to identify elements in the list using their primary key,
# so use a writable field here, rather than the default which would be read-only.
id = serializers.IntegerField()
...
class Meta:
list_serializer_class = BookListSerializer
3.1发行版中可能会包含第三方软件包,该软件包为多种更新操作提供了一些自动支持,类似于allow_add_remove
REST框架2中的行为。
当带有many=True
参数的序列化器被实例化时,我们需要确定哪些参数和关键字参数应该被传递给子Serializer
类和父类ListSerializer
的.__init__()
方法。
默认实现是将所有参数都传递给两个类,除了validators
和自定义的关键字参数,这些参数都假定用于子序列化器类。
有时候,你可能需要明确指定当被传递many=True
参数时,子类和父类应该如何实例化。你可以使用many_init
类方法来执行此操作。
@classmethod
def many_init(cls, *args, **kwargs):
# Instantiate the child serializer.
kwargs['child'] = cls()
# Instantiate the parent list serializer.
return CustomListSerializer(*args, **kwargs)
BaseSerializer
可以简单的用来替代序列化和反序列化的样式。
Serializer
类直接继承了BaseSerializer类,所以两者具有基本相同的API:
.data
- 返回传出的原始数据。.is_valid()
- 反序列化并验证传入的数据。.validated_data
- 返回经过验证后的传入数据。.errors
- 返回验证期间的错误。.save()
- 将验证的数据保留到对象实例中。它还有可以覆写的四种方法,具体取决于你想要序列化类支持的功能:
.to_representation()
- 重写此方法来改变读取操作的序列化结果。.to_internal_value()
- 重写此方法来改变写入操作的序列化结果。.create()
和 .update()
- 重写其中一个或两个来改变保存实例时的动作。因为此类提供与Serializer
类相同的接口,所以你可以将它与现有的基于类的通用视图一起使用,就像使用常规Serializer
或ModelSerializer
一样。
你需要注意到的唯一区别是BaseSerializer
类并不会在可浏览的API页面中生成HTML表单。
BaseSerializer
类要使用BaseSerializer
类实现只读的序列化器,我们只需要覆写.to_representation()
方法。让我们看一个简单的Django模型的示例:
class HighScore(models.Model):
created = models.DateTimeField(auto_now_add=True)
player_name = models.CharField(max_length=10)
score = models.IntegerField()
创建一个只读的序列化器将HighScore
实例转换为原始数据类型很简单。
class HighScoreSerializer(serializers.BaseSerializer):
def to_representation(self, instance):
return {
'score': instance.score,
'player_name': instance.player_name
}
现在,我们可以使用此类来序列化单个HighScore
实例:
@api_view(['GET'])
def high_score(request, pk):
instance = HighScore.objects.get(pk=pk)
serializer = HighScoreSerializer(instance)
return Response(serializer.data)
或使用它来序列化多个实例:
@api_view(['GET'])
def all_high_scores(request):
queryset = HighScore.objects.order_by('-score')
serializer = HighScoreSerializer(queryset, many=True)
return Response(serializer.data)
BaseSerializer
类要创建一个读写都支持的序列化器,我们首先需要实现.to_internal_value()
方法。这个方法返回用来构造对象实例的经过验证的值,如果提供的数据格式不正确,则可能引发ValidationError
。
一旦你实现了.to_internal_value()
方法,那些基础的验证API都会在序列化对象上可用了,你就可以使用.is_valid()
, .validated_data
和 .errors
方法。
如果你还想支持.save()
,你还需要实现.create()
和.update()
方法中的一个或两个。
下面是支持读、写操作的 HighScoreSerializer
完整示例:
class HighScoreSerializer(serializers.BaseSerializer):
def to_internal_value(self, data):
score = data.get('score')
player_name = data.get('player_name')
# Perform the data validation.
if not score:
raise serializers.ValidationError({
'score': 'This field is required.'
})
if not player_name:
raise serializers.ValidationError({
'player_name': 'This field is required.'
})
if len(player_name) > 10:
raise serializers.ValidationError({
'player_name': 'May not be more than 10 characters.'
})
# Return the validated values. This will be available as
# the `.validated_data` property.
return {
'score': int(score),
'player_name': player_name
}
def to_representation(self, instance):
return {
'score': instance.score,
'player_name': instance.player_name
}
def create(self, validated_data):
return HighScore.objects.create(**validated_data)
BaseSerializer
类还可以用来创建新的通用序列化基类来处理特定的序列化样式或者用来整合备用存储后端。
下面这个类是一个可以将任意对象强制转换为基本表示的通用序列化类的示例。
class ObjectSerializer(serializers.BaseSerializer):
"""
A read-only serializer that coerces arbitrary complex objects
into primitive representations.
"""
def to_representation(self, instance):
output = {}
for attribute_name in dir(instance):
attribute = getattr(instance, attribute_name)
if attribute_name.startswith('_'):
# Ignore private attributes.
pass
elif hasattr(attribute, '__call__'):
# Ignore methods and other callables.
pass
elif isinstance(attribute, (str, int, bool, float, type(None))):
# Primitive types can be passed through unmodified.
output[attribute_name] = attribute
elif isinstance(attribute, list):
# Recursively deal with items in lists.
output[attribute_name] = [
self.to_representation(item) for item in attribute
]
elif isinstance(attribute, dict):
# Recursively deal with items in dictionaries.
output[attribute_name] = {
str(key): self.to_representation(value)
for key, value in attribute.items()
}
else:
# Force anything else to its string representation.
output[attribute_name] = str(attribute)
return output
如果你需要自定义序列化类的序列化、反序列化或验证过程的行为,可以通过重写.to_representation()
或.to_internal_value()
方法来完成。
这么做的一些原因包括......
这些方法的签名如下:
.to_representation(self, instance)
接收一个需要被序列化的对象实例并且返回一个序列化之后的表示。通常,这意味着返回内置Python数据类型的结构。可以处理的确切类型取决于你为API配置的渲染类。
可以重写以修改表示样式。例如:
def to_representation(self, instance):
"""Convert `username` to lowercase."""
ret = super().to_representation(instance)
ret['username'] = ret['username'].lower()
return ret
.to_internal_value(self, data)
将未经验证的传入数据作为输入,返回可以通过serializer.validated_data
来访问的已验证的数据。如果在序列化类上调用.save()
,则该返回值也将传递给.create()
或.update()
方法。这是反向的过程,由前端数据往后端保存。
如果任何验证条件失败,那么该方法会引发一个serializers.ValidationError(errors)
。通常,此处的errors
参数将是错误消息字典的一个映射字段,或者是settings.NON_FIELD_ERRORS_KEY
设置的值。
传递给此方法的data
参数通常是request.data
的值,因此它提供的数据类型将取决于你为API配置的解析器类。
与Django表单类似,你可以通过继承扩展和重用序列化类。这允许你在父类上声明一组公共字段或方法,然后可以在许多序列化器中使用它们。举个例子:
class MyBaseSerializer(Serializer):
my_field = serializers.CharField()
def validate_my_field(self, value):
...
class MySerializer(MyBaseSerializer):
...
像Django的Model
和ModelForm
类一样,Meta
序列化程序上的内部类不会隐式继承其父级的内部Meta
类。如果要让Meta
该类从父类继承,则必须明确地这样做。例如:
class AccountSerializer(MyBaseSerializer):
class Meta(MyBaseSerializer.Meta):
model = Account
通常,我们建议不要在内部Meta类上使用继承,而应显式声明所有选项。
此外,以下警告适用于序列化程序继承过程:
Meta
内部类的基类,则仅使用第一个基类。这意味着孩子的Meta
(如果存在),否则就是第一个父类的Meta
等。通过在子类上将名称设置为None
,可以声明性地删除从父类继承的Field
。
class MyBaseSerializer(ModelSerializer):
my_field = serializers.CharField()
class MySerializer(MyBaseSerializer):
my_field = None
但是,你只能使用此黑科技去掉父类显式声明定义的字段;它不会阻止ModelSerializer
生成的默认字段。
初始化序列化程序后,可以使用.fields
属性访问序列化程序上设置的字段的字典。访问和修改此属性使您可以动态修改序列化程序。
访问或修改序列化器的fileds属性可以动态地修改字段。这可以实现一些有趣的操作,比如在运行过程中修改某些字段的参数。
例如,如果您希望能够在初始化时设置序列化程序应使用的字段,则可以创建一个序列化程序类,如下所示:
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
"""
A ModelSerializer that takes an additional `fields` argument that
controls which fields should be displayed.
"""
def __init__(self, *args, **kwargs):
# Don't pass the 'fields' arg up to the superclass
fields = kwargs.pop('fields', None)
# Instantiate the superclass normally
super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)
if fields is not None:
# Drop any fields that are not specified in the `fields` argument.
allowed = set(fields)
existing = set(self.fields)
for field_name in existing - allowed:
self.fields.pop(field_name)
然后,您可以执行以下操作:
>>> class UserSerializer(DynamicFieldsModelSerializer):
>>> class Meta:
>>> model = User
>>> fields = ['id', 'username', 'email']
>>>
>>> print(UserSerializer(user))
{'id': 2, 'username': 'jonwatts', 'email': 'jon@example.com'}
>>>
>>> print(UserSerializer(user, fields=('id', 'email')))
{'id': 2, 'email': 'jon@example.com'}
REST Framework2提供了一个API,允许开发人员重写ModelSerializer
类如何自动生成默认字段集的方式。
该API包括.get_field()
,.get_pk_field()
和其他方法。
由于串行器已经从3.0进行了重新设计,因此该API不再存在。您仍然可以修改创建的字段,但是需要参考源代码,并且要注意,如果您所做的更改是针对API的私有位的,则它们可能会发生变化。