serializers.py

序列化器

我们希望扩展序列化器的实用性。然而,这不是一个简单的问题,需要一些严肃的设计工作。

— Russell Keith-Magee,Django 用户组

序列化器允许将复杂数据(例如查询集和模型实例)转换为本机 Python 数据类型,然后可以轻松地将其呈现为 JSONXML 或其他内容类型。序列化器还提供反序列化,允许在验证传入数据后将解析后的数据转换回复杂类型。

REST 框架中的序列化器的工作方式与 Django 的FormModelForm类非常相似。我们提供了一个Serializer类,它提供了一种强大、通用的方式来控制响应的输出,以及一个ModelSerializer类,它提供了一个有用的快捷方式来创建处理模型实例和查询集的序列化器。

声明序列化器

让我们从创建一个简单的对象开始,我们可以将其用作示例目的

from datetime import datetime

class Comment:
    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()

序列化对象

我们现在可以使用CommentSerializer来序列化注释或注释列表。同样,使用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"}'

反序列化对象

反序列化是类似的。首先,我们将流解析为 Python 原生数据类型...

import io
from rest_framework.parsers import JSONParser

stream = io.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)}

保存实例

如果我们希望能够基于经过验证的数据返回完整对象实例,我们需要实现.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()

有时,你希望你的视图代码能够在保存实例时注入附加数据。此附加数据可能包括当前用户、当前时间或请求数据中未包含的任何其他信息。

你可以在调用.save()时通过包含附加关键字参数来执行此操作。例如

serializer.save(owner=request.user)

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

直接覆盖 .save()

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

请注意,在上面的情况下,我们现在必须直接访问序列化程序 .validated_data 属性。

验证

在反序列化数据时,在尝试访问经过验证的数据或保存对象实例之前,始终需要调用 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_KEY REST 框架设置自定义 non_field_errors 键的名称。

在反序列化项目列表时,错误将作为表示每个反序列化项目的字典列表返回。

对无效数据引发异常

.is_valid() 方法采用一个可选的 raise_exception 标志,如果存在验证错误,它将导致引发 serializers.ValidationError 异常。

REST 框架提供的默认异常处理程序会自动处理这些异常,并且默认情况下会返回 HTTP 400 Bad Request 响应。

# Return a 400 response if the data was invalid.
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 声明,那么如果未包含该字段,则此验证步骤将不会进行。


对象级验证

要执行任何其他需要访问多个字段的验证,请向 `Serializer` 子类添加一个名为 `validate()` 的方法。此方法接受一个参数,即字段值字典。如有必要,它应引发 `serializers.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 = serializers.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` 参数来允许部分更新。

# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': '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)  # 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()

可写嵌套表示

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

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

对于更新,你需要仔细考虑如何处理对关系的更新。例如,如果关系的数据为 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 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

由于嵌套创建和更新的行为可能不明确,并且可能需要相关模型之间的复杂依赖关系,REST 框架 3 要求你始终显式编写这些方法。默认的 ModelSerializer .create().update() 方法不支持可写的嵌套表示。

但是,有可用的第三方包,例如 DRF Writable Nested,它支持自动可写的嵌套表示。

在序列化器中保存多个相关实例的另一种方法是编写处理创建正确实例的自定义模型管理器类。

例如,假设我们希望确保 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'}

可以通过访问 self.context 属性,在任何序列化器字段逻辑中使用上下文词典,例如自定义 .to_representation() 方法。


ModelSerializer

通常,你会希望序列化器类与 Django 模型定义紧密映射。

ModelSerializer 类提供了一个快捷方式,它允许你自动创建一个 Serializer 类,其中字段对应于模型字段。

ModelSerializer 类与常规 Serializer 类相同,除了:

  • 它将根据模型自动为你生成一组字段。
  • 它将自动为序列化器生成验证器,例如 unique_together 验证器。
  • 它包括 .create().update() 的简单默认实现。

声明 ModelSerializer 如下所示

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

默认情况下,类上的所有模型字段都将映射到相应的序列化器字段。

模型上的外键等任何关系都将映射到 PrimaryKeyRelatedField。默认情况下不包括反向关系,除非明确包含在 序列化器关系 文档中指定的关系中。

检查 ModelSerializer

序列化器类生成有用的详细表示字符串,允许你完全检查其字段的状态。当使用 ModelSerializers 时,这特别有用,因为你希望确定自动为你创建的一组字段和验证器是什么。

为此,请使用 python manage.py shell 打开 Django 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())

指定要包括的字段

如果你只想在模型序列化器中使用默认字段的一个子集,则可以使用 fieldsexclude 选项,就像使用 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_nameuserscreated,则这将导致字段 account_namecreated 被序列化。

fieldsexclude 属性中的名称通常会映射到模型类上的模型字段。

或者,fields 选项中的名称可以映射到模型类上存在的不带参数的属性或方法。

从 3.3.0 版本开始,必须提供 fieldsexclude 属性之一。

指定嵌套序列化

默认的 ModelSerializer 使用关系的主键,但您也可以使用 depth 选项轻松生成嵌套表示形式

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

depth 选项应设置为一个整数值,表示在恢复到平面表示形式之前应遍历的关系深度。

如果您想自定义序列化完成的方式,则需要自己定义字段。

明确指定字段

您可以向 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
        fields = ['url', 'groups']

额外字段可以对应于模型上的任何属性或可调用对象。

指定只读字段

您可能希望将多个字段指定为只读。您可以使用快捷方式 Meta 选项 read_only_fields,而不是使用 read_only=True 属性显式添加每个字段。

此选项应为字段名称的列表或元组,并按如下方式声明

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=Truedefault=… 关键字参数。

其中一个示例是与当前经过身份验证的 User 的只读关系,该关系与另一个标识符 unique_together。在这种情况下,您将像这样声明用户字段

user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())

请查阅验证器文档,了解UniqueTogetherValidatorCurrentUserDefault类的详细信息。


其他关键字参数

还有一个快捷方式允许您使用extra_kwargs选项在字段上指定任意其他关键字参数。与read_only_fields的情况一样,这意味着您不需要在序列化器上显式声明该字段。

此选项是一个字典,将字段名称映射到关键字参数的字典。例如

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的默认表示法是使用相关实例的主键。

其他表示法包括使用超链接序列化、序列化完整的嵌套表示或使用自定义表示序列化。

有关详细信息,请参阅序列化器关系文档。

自定义字段映射

ModelSerializer 类还公开了一个 API,您可以在其中进行覆盖,以便在实例化序列化器时更改序列化器字段的自动确定方式。

通常,如果ModelSerializer默认情况下未生成您需要的字段,那么您应该将它们显式添加到类中,或者简单地使用常规Serializer类。但在某些情况下,您可能希望创建一个新基类来定义如何为任何给定模型创建序列化器字段。

serializer_field_mapping

Django 模型字段到 REST 框架序列化器字段的映射。您可以覆盖此映射以更改应为每个模型字段使用的默认序列化器字段。

此属性应为序列化器字段类,该类默认用于关系字段。

对于ModelSerializer,默认为serializers.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_related_field 属性返回序列化器类。

relation_info 参数是一个命名元组,它包含 model_fieldrelated_modelto_manyhas_through_model 属性。

build_nested_field(self, field_name, relation_info, nested_depth)

在设置 depth 选项时,调用以生成映射到关系模型字段的序列化器字段。

默认实现基于 ModelSerializerHyperlinkedModelSerializer 动态创建嵌套序列化器类。

nested_depth 将是 depth 选项的值,减一。

relation_info 参数是一个命名元组,它包含 model_fieldrelated_modelto_manyhas_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

HyperlinkedModelSerializer 类与 ModelSerializer 类类似,但它使用超链接来表示关系,而不是主键。

默认情况下,序列化器将包含 url 字段,而不是主键字段。

url 字段将使用 HyperlinkedIdentityField 序列化器字段表示,模型上的任何关系都将使用 HyperlinkedRelatedField 序列化器字段表示。

您可以通过将主键添加到 fields 选项中来显式包含主键,例如

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

绝对 URL 和相对 URL

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

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

这样做将确保超链接可以包含一个合适的 host 名称,以便生成的表示使用完全限定的 URL,例如

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

而不是相对 URL,例如

/accounts/1/

如果您确实想要使用相对 URL,则应该在序列化器上下文中显式传递 {'request': None}

如何确定超链接视图

需要有一种方法来确定哪些视图应该用于超链接到模型实例。

默认情况下,超链接应对应于与样式 '{model_name}-detail' 匹配的视图名称,并通过 pk 关键字参数查找实例。

您可以使用 extra_kwargs 设置中的 view_namelookup_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 字段的名称默认为“url”。您可以使用 URL_FIELD_NAME 设置全局覆盖此设置。


ListSerializer

ListSerializer 类提供一次序列化和验证多个对象的行为。您通常不需要直接使用 ListSerializer,而应该在实例化序列化器时简单地传递 many=True

当实例化序列化器并传递 many=True 时,将创建一个 ListSerializer 实例。然后,序列化器类成为父 ListSerializer 的子类

以下参数也可以传递给 ListSerializer 字段或传递 many=True 的序列化器

allow_empty

默认情况下,这是 True,但如果您想禁止空列表作为有效输入,则可以将其设置为 False

max_length

默认情况下,这是 None,但如果您想验证列表不包含超过此数量的元素,则可以将其设置为正整数。

min_length

默认情况下,这是 None,但如果您想验证列表不包含少于此数量的元素,则可以将其设置为正整数。

自定义 ListSerializer 行为

在某些情况下,您可能需要自定义 ListSerializer 行为。例如

  • 您希望提供列表的特定验证,例如检查列表中的一个元素是否与另一个元素冲突。
  • 您希望自定义多个对象的创建或更新行为。

对于这些情况,您可以通过在序列化器的 Meta 类上使用 list_serializer_class 选项来修改在传递 many=True 时使用的类。

例如

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 版本一起包含,为多重更新操作提供一些自动支持,类似于 REST 框架 2 中存在的 allow_add_remove 行为。

自定义 ListSerializer 初始化

当实例化 many=True 的序列化器时,我们需要确定应将哪些参数和关键字参数传递给子 Serializer 类的 .__init__() 方法,以及父 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

BaseSerializer 类,可用于轻松支持替代序列化和反序列化样式。

此类实现与 Serializer 类相同的 API 基础。

  • .data - 返回传出的原始表示形式。
  • .is_valid() - 反序列化并验证传入的数据。
  • .validated_data - 返回经过验证的传入数据。
  • .errors - 返回验证期间的任何错误。
  • .save() - 将经过验证的数据持久保存到对象实例中。

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

  • .to_representation() - 覆盖此方法以支持序列化,用于读取操作。
  • .to_internal_value() - 覆盖此方法以支持反序列化,用于写入操作。
  • .create().update() - 覆盖其中一个或两个以支持保存实例。

由于此类提供与 Serializer 类相同的接口,因此可以将其与现有的基于类的通用视图一起使用,就像对常规 SerializerModelSerializer 所做的那样。

在这样做时唯一会注意到的区别是 BaseSerializer 类不会在可浏览 API 中生成 HTML 表单。这是因为它们返回的数据不包括允许将每个字段呈现为合适的 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() 方法。此方法返回将用于构造对象实例的经过验证的值,如果提供的数据格式不正确,可能会引发 serializers.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() 方法来实现。

这可能很有用的原因包括...

  • 为新的序列化器基类添加新行为。
  • 为现有类略微修改行为。
  • 提高经常访问的 API 端点的序列化性能,该端点返回大量数据。

这些方法的签名如下

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)映射到错误消息列表的字典。如果您不需要更改反序列化行为,而是希望提供对象级验证,建议您改写 .validate() 方法。

传递给此方法的 data 参数通常是 request.data 的值,因此它提供的 datatype 将取决于您为 API 配置的解析器类。

序列化器继承

与 Django 表单类似,您可以通过继承扩展和重用序列化器。这允许您在父类中声明一组通用的字段或方法,然后可以在许多序列化器中使用这些字段或方法。例如,

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

    def validate_my_field(self, value):
        ...

class MySerializer(MyBaseSerializer):
    ...

与 Django 的 ModelModelForm 类一样,序列化器上的内部 Meta 类不会隐式继承其父类的内部 Meta 类。如果您希望 Meta 类从父类继承,则必须显式这样做。例如

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

通常,我们建议对内部 Meta 类使用继承,而是显式声明所有选项。

此外,以下注意事项适用于序列化器继承

  • 应用常规 Python 名称解析规则。如果您有多个声明 Meta 内部类的基类,则只使用第一个基类。这意味着子类的 Meta(如果存在),否则是第一个父类的 Meta,依此类推。
  • 通过将子类的名称设置为 None,可以声明性地移除从父类继承的 Field

    class MyBaseSerializer(ModelSerializer):
        my_field = serializers.CharField()
    
    class MySerializer(MyBaseSerializer):
        my_field = None
    

    但是,你只能使用此技术来选择退出父类通过声明性定义的字段;它不会阻止 ModelSerializer 生成默认字段。要选择退出默认字段,请参阅 指定要包含的字段

动态修改字段

在对序列化器进行初始化后,可以使用 .fields 属性访问序列化器上设置的字段字典。访问和修改此属性允许你动态修改序列化器。

直接修改 fields 参数允许你执行一些有趣的操作,例如在运行时而非在声明序列化器时更改序列化器字段上的参数。

示例

例如,如果你希望能够在初始化序列化器时设置序列化器应使用的字段,则可以创建如下序列化器类

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().__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 框架 2 提供了一个 API,允许开发人员覆盖 ModelSerializer 类如何自动生成默认字段集。

此 API 包含 .get_field().get_pk_field() 和其他方法。

由于序列化器已使用 3.0 进行了彻底重新设计,因此此 API 不再存在。你仍然可以修改创建的字段,但你需要参考源代码,并且要注意,如果你所做的更改针对的是私有 API 位,则它们可能会发生变化。


第三方包

以下第三方软件包也可供使用。

Django REST marshmallow

django-rest-marshmallow 软件包为序列化器提供了一种替代实现,使用 python marshmallow 库。它公开了与 REST 框架序列化器相同的 API,并且可以在某些用例中用作替代方案。

Serpy

serpy 软件包是为速度而构建的序列化器的替代实现。 Serpy 将复杂数据类型序列化为简单的本机类型。本机类型可以轻松转换为 JSON 或任何其他所需的格式。

MongoengineModelSerializer

django-rest-framework-mongoengine 软件包提供了一个 MongoEngineModelSerializer 序列化器类,支持将 MongoDB 用作 Django REST 框架的存储层。

GeoFeatureModelSerializer

GeoFeatureModelSerializer 序列化器类支持 GeoJSON 的读写操作,由 django-rest-framework-gis 包提供。

HStoreSerializer

HStoreSerializer 序列化器支持 django-hstore DictionaryField 模型字段及其 schema-mode 特性,由 django-rest-framework-hstore 包提供。

Dynamic REST

dynamic-rest 包扩展了 ModelSerializer 和 ModelViewSet 接口,增加了 API 查询参数,用于过滤、排序,以及包含/排除序列化器定义的所有字段和关系。

Dynamic Fields Mixin

drf-dynamic-fields 包提供了一个混合类,可以根据 URL 参数指定的子集动态限制序列化器的字段。

DRF FlexFields

drf-flex-fields 包扩展了 ModelSerializer 和 ModelViewSet,提供了常用的功能,用于动态设置字段,并将原始字段扩展到嵌套模型,这些功能既来自 URL 参数,也来自序列化器类定义。

Serializer Extensions

django-rest-framework-serializer-extensions 包提供了一组工具,用于通过允许在每个视图/请求的基础上定义字段来简化序列化器。可以将字段列入白名单、列入黑名单,并且可以根据需要扩展子序列化器。

HTML JSON Forms

html-json-forms 包提供了一个算法和序列化器,用于根据(非活动)HTML JSON 表单规范处理 <form> 提交。序列化器有助于处理 HTML 中任意嵌套的 JSON 结构。例如,<input name="items[0][id]" value="5"> 将被解释为 {"items": [{"id": "5"}]}

DRF-Base64

DRF-Base64 提供了一组字段和模型序列化器,用于处理 base64 编码文件的上传。

QueryFields

djangorestframework-queryfields 允许 API 客户端通过包含/排除查询参数指定响应中将发送哪些字段。

DRF Writable Nested

drf-writable-nested 包提供了可写的嵌套模型序列化器,允许使用嵌套相关数据创建/更新模型。

DRF Encrypt Content

drf-encrypt-content 包帮助您加密通过 ModelSerializer 序列化的数据。它还包含一些帮助函数。这有助于您加密数据。