DRF框架基本组件之序列化
扩展serializers的有用性是我们想要解决的问题。但是,这不是一个微不足道的问题,而是需要一些严肃的设计工作。
— Russell Keith-Magee, Django用户组
序列化器允许把像查询集和模型实例这样的复杂数据转换为可以轻松渲染成JSON
,XML
或其他内容类型的原生Python类型。序列化器还提供反序列化,在验证传入的数据之后允许解析数据转换回复杂类型。
REST framework中的serializers与Django的Form
和ModelForm
类非常像。我们提供了一个Serializer
类,它为你提供了强大的通用方法来控制响应的输出,以及一个ModelSerializer
类,它为创建用于处理模型实例和查询集的序列化程序提供了有用的快捷实现方式。
https://q1mi.github.io/Django-REST-framework-documentation/api-guide/serializers_zh/
1,声明序列化类
声明一个序列化类,使用它来序列化和反序列化与对象相对应的数据;声明一个序列化类看起来非常类似于声明一个表单
好的做法是将所有的序列化类写在一个 serializers.py文件中。
from .models import Book from rest_framework import serializers class BookSerializer(serializers.Serializer): title = serializers.CharField() # publishDate = serializers.DateField() price = serializers.CharField() # 多对一字段 publish_name = serializers.CharField(source='publish.name') # publish_city = serializers.CharField(source='publish.city') # 多对多字段 authors = serializers.SerializerMethodField() # 默认方法名为get_<fieldName> def get_authors(self,obj): data = [] for author_obj in obj.authors.all(): author_data = {} author_data['name']=author_obj.name author_data['age']=author_obj.age data.append(author_data) return data
序列化对象与反序列化对象
将模型实例转换为 Python 原生数据类型
serializer = CommentSerializer(comment)
serializer.data
序列化多个对象
queryset = Book.objects.all()
serializer = BookSerializer(queryset, many=True)
serializer.data
反序列化对象:
将原生数据类型恢复成通过验证的数据字典
serializer = CommentSerializer(data=request.data)
serializer.is_valid()
# True;通过验证的数据字典
serializer.validated_data
# False;包含一个代表错误消息的字典
serializer.errors
保存实例
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() 将创建一个新实例或更新现有实例,具体取决于在实例化序列化类时是否传递了现有实例
#save方法的源码
class BaseSerializer(Field): def save(self, **kwargs): if self.instance is not None: self.instance = self.update(self.instance, validated_data) assert self.instance is not None, ( '`update()` did not return an object instance.' ) else: self.instance = self.create(validated_data) assert self.instance is not None, ( '`create()` did not return an object instance.' ) return self.instance
在保存实例的时候注入额外的数据。这些附加数据可能包含当前用户,当前时间或其他任何不属于请求数据的信息。
调用 .create() 或 .update() 时,任何其他关键字参数都将包含在 validated_data 参数中
serializer.save(owner=request.user)
字段验证
反序列化数据的时候,你始终需要先调用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
的名称可以通过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 the start is before the stop. """ 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: # 每间屋子每天只能有1个活动。 validators = UniqueTogetherValidator( queryset=Event.objects.all(), fields=['room_number', 'date'] )
更多信息请参阅 validators文档。
访问初始数据和实例
将初始化对象或者查询集传递给序列化实例时,可以通过.instance
访问。如果没有传递初始化对象,那么.instance
属性将是None
。
将数据传递给序列化器实例时,未修改的数据可以通过.initial_data
获取。如果没有传递data关键字参数,那么.initial_data
属性就不存在。
部分更新
默认情况下,序列化器必须传递所有必填字段的值,否则就会引发验证错误。你可以使用 partial
参数来允许部分更新。
# 使用部分数据更新`comment`
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)
处理嵌套对象
前面的实例适用于处理只有简单数据类型的对象,但是有时候我们也需要表示更复杂的对象,其中对象的某些属性可能不是字符串、日期、整数这样简单的数据类型。
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()
如果嵌套表示可以接收 None
值,则应该将 required=False
标志传递给嵌套的序列化器。
class CommentSerializer(serializers.Serializer): user = UserSerializer(required=False) # 可能是匿名用户。 content = serializers.CharField(max_length=200) created = serializers.DateTimeField()
类似的,如果嵌套的关联字段可以接收一个列表,那么应该将many=True
标志传递给嵌套的序列化器。
class CommentSerializer(serializers.Serializer): user = UserSerializer(required=False) edits = EditItemSerializer(many=True) # edit'项的嵌套列表 content = serializers.CharField(max_length=200) created = serializers.DateTimeField()
为嵌套关系定义.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
ModelSerializer
通常你会想要与Django模型相对应的序列化类。
ModelSerializer
类能够让你自动创建一个具有模型中相应字段的Serializer
类。
这个ModelSerializer
类和常规的Serializer
类一样,不同的是:
- 它根据模型自动生成一组字段。
- 它自动生成序列化器的验证器,比如unique_together验证器。
- 它默认简单实现了
.create()
方法和.update()
方法。 -
从版本 3.3.0 开始,必须提供其中一个属性 fields 或 exclude。
任何关系(如模型上的外键)都将映射到 PrimaryKeyRelatedField。
ModelSerializer 对关系字段的默认表示是使用相关实例的主键。
声明一个ModelSerializer
如下:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
-----------------------------------------------------------
class BookSerializer(serializers.ModelSerializer):
# 一对多和多对多字段默认取其id值,若想提供其他值,需要自定义;其他简单字段也可以显示指定字段以覆盖默认字段
# title = serializers.CharField()
publish = serializers.CharField(source='publish.name')
authors = serializers.SerializerMethodField()
def get_authors(self, obj):
data = []
for author_obj in obj.authors.all():
author_data = {}
author_data['name'] = author_obj.name
author_data['age'] = author_obj.age
data.append(author_data)
return data
class Meta:
model = Book
fields = '__all__'
指定只读字段
你可能希望将多个字段指定为只读,而不是显式的为每个字段添加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类的详细文档,请查阅验证器的文档。
包括额外的上下文
在某些情况下,除了要序列化的对象之外,还需要为序列化程序提供额外的上下文。一个常见的情况是,如果你使用包含超链接关系的序列化程序,这需要序列化器能够访问当前的请求以便正确生成完全限定的URL。
你可以在实例化序列化器的时候传递一个context
参数来传递任意的附加上下文。例如:
serializer = AccountSerializer(account, context={'request': request})