Django RESTFramework -- Serializer

序列化器

序列化器允许将复杂的(如querysets或者模型实例等)数据转换到python原生的数据类型,转换成原生python数据类型之后,就可以方便的渲染成json或者是其它格式的数据。

序列化器也提供了反序列化的功能,可以把解析后的数据转换回复杂类型(数据必须先验证)。

REST framework序列化器的工作与Django的Form和ModelForm类非常相似。我们提供了一个序列化类,它提供了功能强大的通用方法来控制响应输出。还提供了模型序列化器,它为创建用于处理模型实例查询集的 序列化器提供了有用的快捷方式。

声明序列化器

一个简单的对象

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='[email protected]', 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()

序列化对象

from rest_framework.renderers import JSONRenderer

json = JSONRenderer().render(serializer.data)
json
# b'{"email":"[email protected]","content":"foo bar","created":"2016-01-27T15:17:10.375877"}'

反序列化对象

先得到一个字典数据

import io
from rest_framework.parsers import JSONParser
import json as Json
# stream = io.BytesIO(json)
# data = JSONParser().parse(stream)
# 感觉用原生的json模块比较方便
data = Json.loads(json)

用字典数据构造序列化器

serializer = CommentSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# {'content': 'foo bar', 'email': '[email protected]', 'created': datetime.datetime(2012, 08, 22, 16, 20, 09, 822243)}

保存实例

要基于校验后的数据得到一个完整的对象实例,需要重写 .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模型对应,需要保证这些方法能把对象存到数据库中。

 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

之后就可以把通过校验的序列化数据返回对应的对象实例了

comment = serializer.save()

当我们调用.save方法时,如果.instance不存在则调用.create方法,如果.instance存在则调用.update方法。

# .save() will create a new instance.
serializer = CommentSerializer(data=data)

# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)

给.save方法传递额外的属性

有时候可能需要在view代码中给.save方法注入一些参数,如当前用户。

serializer.save(owner=request.user)

所有的关键字参数将包含在.create()或.update()方法的 validtate_data参数中。

直接覆盖.save()方法

有些时候.create()和.update()方法可能没有意义,比如在联系表单中我们不需要创建新实例,而是要发送邮件或其它消息。

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)

我们在.save方法中直访问了.validated_data属性

校验

反序列化时,在范围 validated_data属性或调用save()方法前一定会先调用 is_valid()方法。

如果有也现任何校验错误,.error属性将包含错误信息的字典

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.']}

产生无效数据异常

# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)

字段级的校验

在序列化器的子类中,可以通过添加 .validate_ 方法给 field_name 字段自定义校验方法

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

如果在声明 字段时指定了 required=False,如果该字段不在数据中,将不会执行校验步骤。

对象级的校验

校验时可能需要用到多个字段,这时可以重载 .validate()方法

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

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])
    ...

访问初始数据和实例

当给序列化器的instance参数传递一个实例对象或者查询集合时,序列化器将产生一个.instance的变量,如果没有传递,.instance属性将为None

Serializer(data = {xxxx}) # .initial_data可以访问
Serializer() # .initial_data属性不存在

部分更新

# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': 'foo bar'}, partial=True)

处理嵌套对象

序列化器字段本身就是一个序列化器,可以表示嵌套关系。

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值

class CommentSerializer(serializers.Serializer):
    user = UserSerializer(required=False)  # May be an anonymous user.
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

嵌套的是对象列表

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()

可写的嵌套表示

当处理支持反序列化数据的嵌套表示形式时,嵌套对象的任何错误都将嵌套在嵌套对象的字段名称下。

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.']}

和 .validated_data 属性有相似的嵌套结构

为具有嵌套关系的序列化器写 .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()方法

 def update(self, instance, validated_data):
        profile_data = validated_data.pop('profile')
        # Unless the application properly enforces that this field is
        # always set, the following 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

拓展模型的manager类,封装create方法

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

序列化器中调用 manager 的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']
    )

处理多个对象

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'}
# ]

包含额外的上下文

您可以在实例化序列化程序时通过传递上下文参数来提供任意其他上下文。

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'}

之后就可以在任何地方引用 self.context属性了。

模型序列化器

序列化器常与Django的模型类有紧密的映射关系。

ModelSerializer类提供了便捷方法来自动创建与模型类相对应的字段。

ModelSerializer类和Serializer一样,除了:

  • 基于模型类自动创建字段集
  • 为序列化自动生成校验器,如唯一性校验
  • 包含了简单的.create()和.update()方法

声明ModelSerializer类如下:

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ['id', 'account_name', 'users', 'created']

默认的,所有的模型字段都会映射为对应的序列化类的字段

任何引用关系,如外键将会映射为PrimaryKeyRelatedField.

默认情况下不包括反向关系,除非按照序列化器关系文档中的指定明确包含反向关系。

指定应包含哪些字段

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ['id', 'account_name', 'users', 'created']
        # 包含所有 
        # fields = '__all__'
        

指定不包含的字段

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        exclude = ['users']

指定嵌套序列化

默认的,使用主键来代表引用关系,也可以通过depth选项来生成嵌套关系

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ['id', 'account_name', 'users', 'created']
        depth = 1

显示声明字段

也可以重写或添加一些额外的字段

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,自动生成的字段默认也会被设置成 只读。

附加的关键字参数

class CreateUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['email', 'username', 'password']
        # 除指定read_only外还可以指定其它参数,如queryset等
        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默认使用主键来表示被引用的实力。

有关完整的详细信息,请参见序列化程序关系文档。

序列化字段

序列化器字段处理原始值和内部数据类型之间的转换。 它们还处理验证输入值,以及从其父对象检索和设置值。

核心参数

每个序列化器字段类构造函数都至少接受这些参数。 某些Field类采用其他特定于字段的参数,但应始终接受以下内容:

read_only

只读字段包含在API输出中,但在创建或更新操作期间不应包含在输入中。 错误地包含在序列化器输入中的所有“只读”字段都将被忽略。

默认: False

write_only

中接收输入,不会输出

默认: False

required

是否是必须的

默认: False

allow_null

默认: False

source

将用于填充字段的属性的名称。

validators

验证器

error_messages

initial

你可能感兴趣的:(Django RESTFramework -- Serializer)