序列化器允许将复杂数据(如查询集和模型实例)转换为可以轻松渲染成JSON,XML或其他内容类型的原生Python数据类型。序列化器还提供反序列化,在验证传入的数据之后允许解析数据转换回复杂类型。
REST framework中的序列化器与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="tom@example.com", content="foo bar")

声明一个序列化器,可以使用它来序列化和反序列化与Comment对象相应的数据。

from rest_framework import serilizers


class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

序列化对象

现在就可以使用CommentSerialzier来序列化comment或者comment列表。Serializer类看起来很像使用Form类。

serializer = CommentSerializer(comment)
serializer.data
{'email': 'tom@example.com', 'content': 'foo bar', 'created': '2019-07-26T09:54:33.587347Z'}

此时,已经将模型实例转换为Python原生的数据类型。为了完成序列化过程,我们将数据渲染为json。

from rest_framework.renderers import JSONRenderer

json = JSONRenderer().render(serializer.data)
json
b'{"email":"tom@example.com","content":"foo bar","created":"2019-07-26T09:54:33.587347Z"}'

反序列化对象

反序列化也是类似的,首先将一个流解析为Python原生的数据类型。

from django.utils.six import BytesIO
from rest_framework.parsers import JSONParser

stream = BytesIO(json)
data=JSONParser().parse(stream)
data
{'email': 'tom@example.com', 'content': 'foo bar', 'created': '2019-07-26T09:54:33.587347Z'}

将原生数据恢复为验证数据的字典。

serializer = CommentSerializer(data=data)
serializer.is_valid()
True

serializer.validated_data
OrderedDict([('email', 'tom@example.com'), ('content', 'foo bar'), ('created', datetime.datetime(2019, 7, 26, 9, 54, 33, 587347, tzinfo=<UTC>))])

保存实例

如果我们希望能够返回基于验证数据的完整对象实例,需要实现.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()将创建一个新实例
serializer = CommentSerializer(data=data)

# .save()将更新一个已有的comment实例
serializer = CommentSerializer(comment, data=data)

.create().update()方法都是可选的。可以根据你序列化器类的用例不实现,或实现其中一个或都实现。

将附加属性传递给.save()

有时你会希望你的视图代码能够在保存实例时注入额外的数据,此额外数据可能包括当前用户,当前时间或不是请求数据一部分的其他信息。
你可以通过在调用.save()时包含其他关键字参数来执行操作。

serializer.save(owner=request.user)

.create().upadte被调用时,任何其他关键字参数将被包含在validated_data参数中。

直接重新.save()

在某些情况下,.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属性。

验证

在反序列化数据时,在尝试访问经过经验的数据或保存对象实例之前,总是需要调用.is_valid()。如果发生任何验证错误,.errors属性将包含表示结果错误消息的字典,例如:

serializer = CommentSerializer(data={"email": "foobar", "content": "baz"})

serializer.is_valid()
False

serializer.errors
{'email': [ErrorDetail(string='Enter a valid email address.', code='invalid')], 'created': [ErrorDetail(string='This field is required.', code='required')]}

字典中的每个键都是字段名称,值是与该字段对应的任何错误信息的字符串列表。non_field_errors键也可能存在,并列出任何常规验证错误。可以使用REST framework设置中的NON_FILE_ERRORS_KEY来自定义non_field_errors键的名称。
当反序列化项目列表时,错误将作为表示反序列化项目的字典列表返回。

引发无效数据异常

.is_valid()方法使用可选的raise_exception标志,如果存在验证错误,将会抛出serializers.ValidationError异常。
这些异常由REST framework提供的默认异常处理程序自动处理,默认情况下将返回HTTP 400 Bad Request异常。

serializer.is_valid(raise_exception=True)

字段级别验证

可以通过向Serializer子类中添加.validate_<field_name>方法来指定自定义字段级的验证。这些类似于Django表单中的.clean_<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):
        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):
        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:
        validators = UniqueTogetherValidator(
            queryset=Event.objects.all(),
            fields=['room_number', 'date']
        )

访问初始数据和实例

将初始化对象或者查询集传递给序列化器实例时,该对象将以.instance的形式提供,如果没有传递初始化独享,那么.instance属性将是None。
将数据传递给序列化器实例时,未修改的数据将以.initial_data的形式提供。如果data关键字参数未被传递,那么.initial_data属性将不存在。

部分更新

默认情况下,序列化器必须传递所有必填字段的值,否则就会引发验证错误。可以使用partial参数以允许部分更新。

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)
    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': [u'Enter a valid e-mail address.']}, 'created': [u'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()方法

对于更新,您需要仔细考虑如何处理关系更新。例如,如果关系的数据为 None 或未提供,则应发生以下哪种情况?

  • 在数据库中将关系设置为 NULL。
  • 删除关联的实例。
  • 忽略数据并保留这个实例。
  • 抛出验证错误。
    上面UserSerializer类中的update()方法示例:

      def update(self, instance, validated_data):
          profile_data = validated_data.pop('profile')
          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()方法不包括对可写嵌套表示的支持。

    处理在模型管理类中保存关联实例

    在序列化器中保存多个相关实例的另一种方法是编写处理创建正常实例的自定义模型管理器。
    例如,假设我们希望确保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']
      )
    

处理多个对象

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

反序列化多个对象

反序列化多个对象的默认行为是支持多个对象创建,但不支持多个对象更新。

包括额外的上下文

在某些情况下,除了要序列化的对象之外,还需要为器提供额外的上下文。一种常见的情况是,如果你正在使用包含超链接关系的序列化器,这需要序列化能够访问当前的请求以便正确生成完全限定的URL。
可以通过在实例化序列化器时传递context参数来提供任意的附加上下文。
示例:

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

ModelSerialier

通常,会希望序列化器类紧密地映射到Django模型定义上。
Modelserilizer类提供了一个快捷方式,可以自动创建具有与模型字段对应的字段的Serializer类。
ModelSerializer类与Serializer的不同点在于:

  • 将根据模型自动生成一组字段。
  • 将自动为序列化器生成验证器。
  • 包含默认简单实现的.create().update()方法。

示例:

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

默认情况下,类上的所有模型字段都将映射到相应的序列化器字段。
模型上任何关系(如,外键)都将映射到PrimaryKeyRelatedField。默认情况下,不包含反向关系,除非在序列化器关系中明确包含指定。

检查ModelSerializer

序列化器类生成有用的详细表示字符串,允许全名检查其字段的状态。在使用ModelSerilizer时特别有用。
查看上面示例中序列化器的对象表示:

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

指定要包含的字段

如果只想在模型序列化器中使用默认字段的子集,则可以像使用ModelForm一样,使用fields或exclude选项。强烈建议使用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属性设置为从序列化器中排除的字段列表。

calss AccountSerializer(serializers.ModelSerializer):
    class Meta:
        mode = Account
        exclude = ("users", )

从3.3.0版本开始,必须提供fields或者exclude其中一个属性。

指定嵌套序列化

默认的ModelSerializer使用主键进行关联,但也可以使用depth选项生成嵌套关联:

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

depth选项应设置为一个整数值,代表嵌套关系的底基层。当定义了depth时,这个序列化器类中引用字段(外键)则自动变为只读。

显式指定字段

你可以向ModelSerialzier添加额外字段,或通过在类上声明字段来重写默认字段。

class AccountSerializer(serializers.ModelSerializer):
    url = serializers.CharField(source="get_absolute_url", read_only=True)
    groups = serializers.PrimaryKeyRelatedField(many=True)

    class Meta:
        model = Account

额外的字段可以队形模型上任何属性或可调用的字段。

指定只读字段

你可能希望将多个字段指定为只读,可以使用Meta选项的read_only_fields,而不是使用read_only属性显式的添加每个字段。该选项是一个字段名称的列表或者元组:

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Accout
        fieds = ("id", "account_name", "users", "created")
        read_only_fields = ("account_name",)

模型中已经设置editable=False的字段的默认就被设置为只读的AutoField字段都不需要添加到read_only_fields选项中。
注意,有一种特殊情况,其中只读字段是模型级别unique_together约束的一部分。在这种情况下,序列化器类需要该字段来验证约束,但也不能由用户编辑。
处理该问题的正确方法是在序列化器上显示指定字段,同时提供read_only=Truedefault=...关键字参数。

附加关键字参数

还有一个快捷方式允许你使用extra_kwargs选项在字段上指定任意附加关键字参数。与read_only_fields的情况一样,这意味着不需要在序列化器中显式声明该字段。
此选项是一个字段,将字段名称映射到关键字参数的字段。

class CreateUserSerailizer(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_date["username"]
        )
        user.set_password(validated_data["password"])
        user.save()
        return user

关系字段

序列化模型示例时,可以选择多种不同的方式来表示关联关系。ModelSerializer的默认表示是使用相关实例的主键。
替代表示方式包括使用超连接序列化,序列化完整的嵌套表示或者使用自定义表示的序列化。

自定义字段映射

ModelSerializer类还提供了一个可以重写的API,以便在实例化序列化器时更改如何自动确定序列化器字段。通常,如果ModelSerailizer默认情况下没有生成你需要的字段,那么应该将它们显式地添加到类中,或者简单地使用常规Serializer类。但是在某些情况,可能需要创建一个新的基类,类定义如何为任意给定模型创建序列化字段。
.serializer_field_mapping
Django模型类到REST framework序列化器类的映射。可以重写此映射以更改应该用于每个模型类的模型默认序列化器类。
.serializer_related_field
此属性应是序列化器字段类,默认情况下用于关联字段。
对于ModelSerializer此属性默认是PrimaryKeyRelatedField。对于HyperlinkedModelSerializer此属性时serializers.HyperlinkedRelatedField
serializer_url_field
应该用于序列化器上任何url字段的序列化器字段类。
默认是serializers.HyperlinkedIdentityField
serializer_choice_field
应用于序列化器上任何选择字段的序列化器字段类。
默认是serializers.ChoiceField

field_class和field_kwargs API

调用下面的方法来确定应该自动包含在序列化器中每个字段的类和关键字参数。这些方法都应该返回(field_class, field_kwargs)元组。
.build_standard_field(self, field_name, model_field)
调用以到生成映射到标准模型字段的序列化器字段。默认实现返回基于serializer_field_mapping属性的序列化器类。
.build_relational_field(self, field_name, relation_info)
调用以生成映射到关系模型字段的序列化器字段。默认实现返回基于serializer_relationak_field属性的序列化器类。
relation_info参数是要一个命名元组,包含model_fieldrelated_modelto_manyhas_through_model属性。
.build_property_field(self, field_name, model_class):调用以生成映射到模型类中的属性或零方法的序列化器字段。默认实现返回ReadOnlyFied类。
.build_url_field(self, field_name, model_class):调用为序列化器自己的url字段生成序列化器字段。默认实现返回HypelinkedIdentityField类。
.build_unknown_field(self, field_name, model_class):当字段名称未映射到任何模型字段或模型属性时调用。默认是实现会引发错误,但子类可以自定义此行为。

HyperlinkedModelSerializer

HyperlinkedModelSerializer类类似于ModelSerializer类,不同之处在于它使用超连接来表示关联关系而不是主键。
默认情况下,序列化器将包含url字段而不是主键字段。
url字段使用HyperlinkedIdentityField序列化器字段表示,并且模型上的任何关系将使用HyperlinkedRelatedField序列化器字段表示。
可以通过将主键添加到fields选项显式包含主键:

class AccountSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Account
        fields = ("url", "id", "account_name", "users", "created")

绝对和相对URL

当实例化HyperlinkedModelSerializer时,必须在序列化器上下文中包含当前request。
示例:

serializer = AccountSerializer(queryset, context={"request": request})

这样做将确保超链接可以包含适当的主机名,以便生成完全限定的URL,如:

http://api.example.com/accounts/1/

而不是相对URL,例如:

/accounts/1/

如果确实想使用相对URL,应在序列化器上下文中显示传递{"request": None}

如何确定超链接视图

需要有种方法来确定应该将那些视图用于超链接到模型实例。
默认情况下,超链接预期对应于匹配样式{model_name}-detail的视图名,并通过pk关键字参数查找实例。
可以使用在extra_kwargs设置中的view_name和lookup_field选项中的一个或两个来重写URL字段视图名称和传字段。
示例:

class AccountSerializer(serialziers.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"}
        }

或者,可以显示设置序列化器上的字段:

calss AccountSerializer(serializers.HyperlinkedModelSerialzizer):
    url = serializers.HyperlinkedIdentityField(
        view_name="accounts",
        lookup_field="slug"
    )
    users = serializers.HyperlinkedRelatedField(
        view_name="user-detail",
        look_field="username",
        many=True,
        read_only=True
    )

    class Meta:
        model = Account
        fields = ("url", "account_name", "users", "created")

更改URL字段名称

URL字段名称默认为url,可以通过使用URL_FIELD_NAME设置进行全局覆盖。

ListSerializer

ListSerializer类提供同时序列化和验证多个对象的行为。通过不需要直接使用ListSerializer,而应该在实例化序列化器时简单地传递many=True
当序列化器被实例化并且many=True被传递时,ListSerializer实例将被创建。然后,序列化器类将成为ListSerializer的子类。
可选项参数:
allow_empty:默认情况下为True,但如果要禁止空列表作为有些输入,则可以设置为False。

自定义ListSerializer行为

有些情况下,可能想要自定义ListSerializer行为,例如:

  • 想要提供列表的特定认证,例如检查要结果元素是否与列表中的另一个元素冲突。
  • 想要自定义多个对象的创建或更新行为。
    对于这些情况,可以通过使用序列化器Meta类中的list_serializer_class选项来修改传递many=True时使用的类:
    class CustomListSerializer(serializers.ListSerializer):
      ...
    class CustomSerializer(serializers.Serializer):
      ...
      class MetaL
          list_serializer_class = CustomSerializer
    

    自定义多重创建

    对个对象的创建默认实现是简单地为列表中的每个项目调用.create()。如果想要自定义该行为,那么需要自定义当被传递many=True参数使用的ListSerializer类中.create()方法。
    示例:
    ```python
    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类不支持多个更新。这是因为插入和删除预期的行为不明确。
为了支持多个更新,需要明确地进行这样的操作,并需要考虑以下几个问题:
- 如何确定为数据列表中的每个item更新哪个实例?
- 如何处理插入?它们是无效的,还是创建新对象?
- 如何处理删除?它们是按时删除对象还是删除关系?它们应该被忽略还是无效?
- 如何处理排序?改变两个项目的位置是否意味着任何状态改变或被忽略?

需要向实例序列化器显示添加id字段。默认隐式生成的`id`字段被标记为`read_only`。这会导致它在更新时被删除。一旦显式地声明它,它将在列表序列化器的update方法中可用。
示例:
```python
class BookListSerializer(serializers.ListSerializer):
    def update(self, instance, validated_data):
        book_mapping = {book.id: book for book in instance}
        data_mapping = {item["id"]: item for item in validated_data}

        # 创建和更新
        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))

        # 删除
        for book_id, book in book_mapping.items():
            if book_id not in data_mapping:
                book.delete()

        return ret


class BookSerializer(serializers.Serializer):
    id = serializer.IntegerField()
    ...

    class Meta:
        list_serializer_class = BookListSerializer

自定义ListSerializer初始化

当带有many=True的序列化器被实例化是,需要确定哪些参数和关键字参数应该被传递给子类Serializer和父类ListSerializer的__init__()方法。
默认实现是将所有参数传递给两个类,除了validators和任何自定义关键字参数,这两个参数都假定是用于子类序列化器的。
偶尔,可能需要显式的指定当被传递many=True参数时,子类和分类应该如何实例化。可以使用many_init类方法执行此操作。

@classmethod
def many_init(cls, *args, **kwargs):
    kwargs["child"] = cls()
    return CustomListSerializer(*args, **kwargs)

BaseSerializer

BaseSerializer类可用于轻松支持替代序列化和反序列化样式的类。
该类实现与Serializer类基本相同:

  • .data:返回传出的基元表示。
  • .is_valid():反序列化和验证传入数据。
  • .validated_data:返回经过验证的传入数据。
  • .errors:在验证期间返回任何错误。
  • .save():将验证的数据保留到对象实例中。

可覆盖四种方法,具体取决于希望序列化器类支持的功能:

  • .to_representation():覆盖此选项以支持序列化,以进行读取操作。
  • .to_internal_value():覆盖它以支持反序列化,用于写操作。
  • .create().update():覆盖其中一个或两个一支持保存实例。

因为此类提供了与Serialzer类相同的接口,所以可以像使用常规的Serializer或ModelSerializer一样使用现有的通用的基于类的视图。
在执行操作时,需要注意的唯一区别是:BaseSerializer类不会再可浏览的API中生成HTML表单。这是因为它们返回的数据不包括允许每个字段被渲染成合适的HTML输入的所有字段信息。

只读BaseSerializer类

基于BaseSerializer的只读序列化器,只需要重写.to_representation()方法。
模型类示例:

class HighScore(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    player_name = models.CharField(max_length=10)
    score = models.IntegerField()

序列化器类示例:

class HighScoreSerializer(serializers.BaseSerializer):
    def to_representation(self, obj):
        return {
            "score": obj.score,
            "player_name": obj.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_score(request):
    queryset = HighScore.objects.order_by("-score")
    serializer = HighScoreSerializer(queryset, many=True)
    return Response(serializer.data)

读写BaseSerializer类

要创建读写序列化器,首先需要实现.to_internal_value()方法。此方法返回将用于构造对象实例的验证值,如果提供的数据格式不正确,则可能引发serialziers.ValidationError
一旦实现.to_internal_value(),基本验证API将在序列化器中可用,并且还能够使用.is_valid().validated_data().errors()
如果还想支持.save(),还需要实现.create().update()方法中的一个或两个。
示例:

class HighScoreSerializer(serializers.BaseSerializer):
    def to_internal_value(self, data):
        score = data.get("score")
        player_name = data.get("player_name")

        # 数据验证
        if not score:
            raise serializer.ValidationError({
                "score": "This field is requeired."
            })

        if not player_name:
            raise serializer.ValidationError({
                "player_name": "This field is requeired."
            })

        if len(player_name) > 10:
            raise serializer.ValidationError({
                "player_name": "May not be more than 10 characters."
            })

        # 返回验证值,将作为.validated_data属性
        return {
            "score": int(score),
            "player_name": player_name
        }

    def tp_representation(self, obj):
        return {
            "score": obj.score,
            "player_name": obj.player_name
        }

    def create(self, validated_data):
        return HighScore.objects.create(**validated_data)

创建新的基类

如果想要实现新的通用序列化器类来处理特定的序列化样式,或者集成其他存储后端,那么BaseSerializer类也很有用。
示例:

class ObjectSerializer(serializers.BaseSerializer):
    """
    任意复杂对象强制转换为原始表示的只读序列化器
    """
    def to_representation(self, obj):
        output = {}
        for attribute_name in dir(obj):
            attribute = getattr(obj, attribute_name)
            if attribute_name.startswith("_"):
                # 忽略私有方法
                pass
            elif hasattr(attribute, "__call__"):
                # 忽略方法和其他callables
                pass
            elif isinstance(attribute, (str, int, bool, float, type(None))):
                # 原始类型可以通过未修改的方式传递
                output[attribute_name] = attribute
            elif isinstance(attribute, list):
                # 递归处理列表中项
                output[attribute_name] = [
                    self.tp_representation(item) for item in attribute
                ]
            elif isinstance(attribute, dict):
                # 递归处理字典中项
                output[attribute_name] = {
                    str(key): self.to_representation(value)
            else:
                # 将其他内容强制到其字符串表示形式
                output[attribute_name] = str(attribute)
        return output

高级序列化器用法

重写序列化和反序列化行为

如果需要更改序列化器类的序列化或反序列化行为,可以通过重写.to_representation().to_internal_value()来完成。
可能有用的一些原因包括:

  • 为新的序列化器基类添加新行为。
  • 对现有类稍微修改行为。
  • 提高频繁访问返回大量数据的API端点的序列化性能。

这些方法的明显特征如下:
.to_representation(self, obj)
获取需要序列化的对象实例,并返回原始表示。通常,这意味着返回内置Python数据类型结构。可以处理的确切类型取决于API配置的渲染器类。
示例:

def to_representation(self, instance):
    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)映射到错误消息列表的字典。如果不需要改变反序列行为,而是想提供对象级别的验证,则建议改为重写.validate()方法。
传递给此方法的data参数通常是request.data的值,因此它提供的数据类型将去去鳄鱼为API配置的解析器类。

序列化器继承

class MyBaseSerializer(Serializer):
    my_field = serializers.CharField()

    def validate_my_field(self):
        ...


class MySerializer(MyBaseSerializer):
    ...

与Django的Model和ModelForm类一样,序列化器内部Meta类不会隐式地继承她父类内部Meta类。如果想要从父类继承Meta,则必须明确这样做,示例:

class AccountSerializer(MyBaseSerializer):
    class Meta(MyBaseSerializer.Meta):
        model = Account

通常建议不要在内部Meta类上使用继承,而是显式声明所有选项。
此外,一下注意是一项适用于序列化程序继承:

  • 正常的Python名称解析规则适用。如果有多个声明Meta内部类的基类,则使用第一个类。
  • 通过在子类上将名称设置为None,声明性地删除继承自父类的Field是可能的。
class MyBaseSerializer(ModelSerializer):
    my_field = serializers.CharField()

class MySerializer(MyBaseSerializer):
    my_field = None

但是,只能使用者何种技术选择从父类声明性定义的字段,它不会阻止ModelSerializer生成默认字段。

动态修改字段

一旦序列化器已初始化,可以使用.field属性访问序列化器上设置的字段字典。访问和修改此属性允许动态修改序列化器。
示例

class DynamicFieldsModelSerializer(serializers.ModelSerializer):
    def __init__(self, *args, **kwargs):
        fields = kwargs.pop("fields", None)
        super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)

        if fields is not NoneL
            allowed = set(fields)
            existing = set(self.fields)
            for field_name in exsiting - 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'}
0条评论

相关推荐

django教程

r

Django 2019-05-20 10:53:53

Celery

celery学习资料

Django 2019-05-25 18:41:55

django教程入门

适合django新人使用的学习手册

Django 2019-07-01 14:28:04

django实用资料

django项目从0到1自己总结的实用的资料,大部分常用的功能这里都有

Django 2019-05-08 18:21:34