【djangorestframework】5、Serializers-Serializer(序列化器)
序列化器(Serializers)
- 扩展序列化器的有用性是我们想要解决的问题。人儿,这不是一个微不足道的问题,它将需要一些严肃的的设计工作。---Ressell Keith-Magee,Django用户组
 - 序列化器允许将复杂数据(如查询集和模型实例)转换为可以轻松渲染成JSON、XML或其他内容类型的原生Python数据类型。序列化器还提供反序列化,在验证传入的数据之后允许解析数据转换回复杂类型。
 - REST framework中的序列化器与Django的Form和ModelForm类非常相似。我们提供了一个Serializer类,它为你提供了强大的、通用的方法来控制响应的输出,以及一个ModelSerializer类,它为创建用于处理模型实例和查询集的序列化器提供了有用的快捷实现方式。
 
声明序列化器(Declaring Serializers)
- 让我们从创建一个我们可以用于示例目的的简单对象开始
 
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()方法都是可选的。你可以根据你序列化器的用例不实现或实现其中一个或都实现。
 
将附加属性传递给.save()(Passing additional attributes to .save())
- 有时你会希望你的视图代码能够在保存实例时注入额外的数据。此额外的数据可能包括当前用户,当前时间或不是请求数据一部分的其他信息。
 - 你可以通过在调用.save()时包含其他关键字参数来执行此操作。例如:
 
serializer.save(owner=request.user)- 在.create()或.update()被调用时,任何其他关键字参数将被包含在validated_data参数中
 
直接重写.save()(Overriding .save() directly)
- 在某些情况下,.create()或.update()方法名称可能没有意义。例如在contact form中,我们可能不会创建新的实例,而是发送电子邮件或其他消息。
 - 在这些情况下,你可以直接重写.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)- 请注意,在上面的情况下,我们现在必须直接访问序列化器.validated_data属性。
 
验证(Validation)
- 在反序列化数据时,在尝试访问经过验证的数据或保存对象实例之前,总是需要调用is_valid()。如果发生任何验证错误,.errors属性将包含表示结果错误消息的字典。例如:
 
serializer.is_valid()
# False
serializer.errors
# {'email': [u'Enter a valid e-mail address.'], 'created': [u'This field is required.']}- [错误消息的]字典中的每个键都是字段名称,值是与该字段对应的任何错误消息的字符串列表。non_field_errors键也可能存在,并将列出任何常规验证错误。可以使用REST framework设置中的NON_FIELD_ERRORS_KEY来自定义non_field_errors键的名称。
 - 当反序列化项目列表时,错误将作为表示每个反序列化项目的字典列表返回。
 
引发无效数据的异常(Raising an exception on invalid data)
- .is_valid()方法使用可选的raise_exception标志,如果存在验证错误,将会抛出serializers.ValidationError异常。
 - 这些异常由REST framework提供的默认异常处理程序自动处理,默认情况下,返回HTTP_400_Bad_Request响应。
 
# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)字段级别验证(Field-level validation)
- 你可以通过向你的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,那么如果不包含该字段,则此验证步骤不会发生。
 
对象级别验证(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)
- 默认情况下,序列化器必须传递所有必填字段的值,否则就会引发验证错误。你可以使用partial参数以允许部分更新。
 
# 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()- 如果嵌套表示可以可选地接受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()可写的嵌套表示(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属性将包含嵌套数据接口。
 
为嵌套表示编写.create()方法(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为嵌套表示编写.update()方法(Writing .update() methods for nested representations)
- 对于更新,你需要仔细考虑如何处理关系更新。例如,如果关系的数据为None或未提供,则应发生以下哪种情况?
 - 在数据库中将关系设置为Null
 - 删除关联的实例
 - 忽略数据并保留这个实例
 - 抛出验证错误。
 - 这是我们之前UserSerializer类中update()方法的示例。
 
def update(self, instance, validated_data):
        profile_data = validated_data.pop('profile')
        # 除非应用程序正确地强制始终设置该字段,否则就应该抛出一个需要处理的`DoesNotExist`。
        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 framework3要求你始终显示的编写这些方法。默认的ModelSerializer .create()和.update()方法不包括对可写嵌套表示的支持。
 - 但是,可用的第三方软件包(如DRF Writable Nested)支持自动可写嵌套表示。
 
处理在模型管理类中保存关联实例(Handling saving related instances in model manager classes)
- 在序列化器中保存多个相关实例的另一种方法是编写处理创建正确实例的自定义模型管理器类。
 - 例如,假设我们希望确保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- 此管理器类现在更好地封装了user实例和profile实例始终同时创建。现在可以重写我们在序列化程序类上的.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文档,以及使用模型和管理器类的相关博客。
 
处理多个对象(Dealing with multiple objects)
- Serializer类还可以处理序列化或反序列化对象列表。
 
序列化多个对象(Serializing multiple objects)
- 要序列化查询集或对象列表而不是单个对象实例,应在实例化序列器时传递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'}
# ]反序列化多个对象(Deserializing multiple objects)
- 反序列化多个对象的默认行为是支持多个对象创建,但不支持多个对象更新。有关如何支持或自定义这些情况的更多信息,请参阅下面的ListSerializer文档。
 
包括额外的上下文(Including extra context)
- 在某些情况下,除了要序列化的对象之外,还需要为序列化器提供额外的上下文。一种常见的情况是,如果你正在使用包含超链接关系的序列化器,这需要序列化器能够访问当前的请求以便正确生成完全限定的URL
 - 你可以通过在实例化序列化器时传递context参数来提供任意的附加上下文。例如:
 
erializer = AccountSerializer(account, context={'request': request})
serializer.data
# {'id': 6, 'owner': u'denvercoder9', 'created': datetime.datetime(2013, 2, 12, 09, 44, 56, 678870), 'details': 'http://example.com/accounts/6/details'}- 通过访问self.context属性,可以在任何序列化器字段逻辑中使用上下文字典,例如自定义的.to_representation()方法。
 
本文来自博客园,作者:郭祺迦,转载请注明原文链接:https://www.cnblogs.com/guojie-guojie/p/16190612.html
                    
                
                
            
        
浙公网安备 33010602011771号