Django-13-DRF深入序列化器

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

一、序列化程序

1.1 声明序列化程序

1.1.1 创建一个serializers.py文件

from datetime import datetime

from django.contrib.auth.models import Group, User
from rest_framework import serializers

from news.models import News


# 定义一个类,这个类我们用于序列化和反序列化
class Comment:
    def __init__(self, email, content, created=None):
        self.email = email
        self.content = content
        self.created = created or datetime.now()


# 定义一个序列化器
class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

1.1.2 打开django的shell终端

Django-13-DRF深入序列化器
image.png

1.2 序列化对象

>>> from news.serializers import Comment
>>> comment = Comment(email='leila@example.com', content='foo bar')
>>> serializer = CommentSerializer(comment)
>>> serializer.data
{'email''leila@example.com''content''foo bar''created''2023-12-13T13:36:31.317618Z'}
>>> serializer
CommentSerializer(<news.serializers.Comment object>):
    email = EmailField()
    content = CharField(max_length=200)
    created = DateTimeField()
>>>
Django-13-DRF深入序列化器
image.png


我们已将模型实例转换为 Python 本机数据类型。为了完成序列化过程,我们将数据转换为JSON


>>> from rest_framework.renderers import JSONRenderer
>>> json = JSONRenderer().render(serializer.data)
>>> json
b'{"email":"leila@example.com","content":"foo bar","created":"2023-12-13T13:36:31.317618Z"}'
>>>
Django-13-DRF深入序列化器
image.png

1.3 反序列化对象

反序列化与序列化相反,我们将流解析为 Python 原生数据类型。
我们把如下json转换成对象,就是反序列化对象

b'{"email":"leila@example.com","content":"foo bar","created":"2023-12-13T13:36:31.317618Z"}'

反序列化代码

import io
from rest_framework.parsers import JSONParser
json = b'{"email":"leila@example.com","content":"foo bar","created":"2023-12-13T13:36:31.317618Z"}'
stream = io.BytesIO(json)
data = JSONParser().parse(stream)
print(data)
#{'email': 'leila@example.com', 'content': 'foo bar', 'created': '2023-12-13T13:36:31.317618Z'}
Django-13-DRF深入序列化器
image.png


上述代码已经把字符串反序列化成了一个字典了。
那如何把这个反序列化后的字段,转换成经过验证的序列化对象呢?


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

serializer.validated_data 
#OrderedDict([('email', 'leila@example.com'), ('content', 'foo bar'), ('created', datetime.datetime(2023, 12, 13, 13, 36, 31, 317618, tzinfo=zoneinfo.ZoneInfo(key='UTC')))])
Django-13-DRF深入序列化器
image.png

1.4 保存实例

如果我们希望能够根据经过验证的数据返回完整的对象实例,我们需要实现一个或两个和方法。例如:.create()``.update()

from datetime import datetime

from django.contrib.auth.models import Group, User
from rest_framework import serializers

from news.models import News


# 定义一个类,这个类我们用于序列化和反序列化
class Comment:
    def __init__(self, email, content, created=None):
        self.email = email
        self.content = content
        self.created = created or datetime.now()



# 定义一个序列化器
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 模型,你还需要确保这些方法将对象保存到数据库中。例如,如果是 Django 模型,则方法可能如下所示

# 定义一个序列化器
class CommentSerializer(serializers.Serializer):
    email = serializers.EmailField()
    content = serializers.CharField(max_length=200)
    created = serializers.DateTimeField()

    def create(self, validated_data):
        # 这里与django模型关连
        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)
        # 这里是更新到django模式上
        instance.save()
        return instance

1.5 验证

反序列化数据时,始终需要在尝试访问已验证的数据或保存对象实例之前进行调用。如果发生任何验证错误,该属性将包含一个字典,表示生成的错误消息。例如:is_valid()``errors

serializer = CommentSerializer(data={'email''foobar''content''baz'})
serializer.is_valid()
# False
# 当反序列化验证为false,可以通过序列化对象的errors得到错误信息
serializer.errors
# {'email': ['Enter a valid e-mail address.'], 'created': ['This field is required.']}

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

1.5.1 引发无效数据异常

该方法采用一个可选标志,如果存在验证错误,该标志将导致它引发异常。.is_valid()``raise_exceptionserializers.ValidationError
这些异常由 REST 框架提供的默认异常处理程序自动处理,并且默认情况下将返回响应。HTTP 400 Bad Request

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

1.5.2 字段级验证

您可以通过向子类添加方法来指定自定义字段级验证。这些类似于 Django 表单上的方法。.validate_<field_name>Serializer.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,则在未包含该字段的情况下,将不会执行此验证步骤。

1.5.3 对象级验证

若要执行需要访问多个字段的任何其他验证,请向子类添加调用的方法。此方法采用单个参数,该参数是字段值的字典。如有必要,它应该引发一个,或者只返回经过验证的值。例如:.validate()``Serializer``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

1.6 部分更新

默认情况下,必须为序列化程序传递所有必填字段的值,否则它们将引发验证错误。您可以使用该参数来允许部分更新。partial

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

1.7 处理嵌套对象

有时我们还需要能够表示更复杂的对象,其中对象的某些属性可能不是简单的数据类型,例如字符串、日期或整数。
该类本身就是一种 ,可用于表示一种对象类型嵌套在另一种对象类型中的关系。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()

1.8 可写嵌套表示

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

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

1.8.1 为嵌套表示编写.create()方法

如果支持可写嵌套表示形式,则需要编写处理保存多个对象的方法。.create()``.update()
下面的示例演示如何处理使用嵌套配置文件对象创建用户。

class UserSerializer(serializers.ModelSerializer):
    # 嵌套了一个序列化器
    profile = ProfileSerializer()

    class Meta:
        # 模型使用User中的模型
        model = User
        # 字段使用User模型中的如下字段
        fields = ['username''email''profile']

    # 创建
    def create(self, validated_data):
        # 得到验证后的profile数据
        profile_data = validated_data.pop('profile')
        # 创建用户
        user = User.objects.create(**validated_data)
        # 创建Profile
        Profile.objects.create(user=user, **profile_data)
        return user

1.8.2 为嵌套表示编写.update()方法

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

  • 将关系设置为 在数据库中。NULL

  • 删除关联的实例。

  • 忽略数据并保留实例原样。

  • 引发验证错误。

下面是我们上一个类中的方法示例。.update()``UserSerializer

class UserSerializer(serializers.ModelSerializer):
    # 嵌套了一个序列化器
    profile = ProfileSerializer()

    class Meta:
        # 模型使用User中的模型
        model = User
        # 字段使用User模型中的如下字段
        fields = ['username''email''profile']

    # 创建
    def create(self, validated_data):
        # 得到验证后的profile数据
        profile_data = validated_data.pop('profile')
        # 创建用户
        user = User.objects.create(**validated_data)
        # 创建Profile
        Profile.objects.create(user=user, **profile_data)
        return user

    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

1.8.3 处理在模型管理器类中保存相关实例

在序列化程序中保存多个相关实例的替代方法是编写自定义模型管理器类,以处理创建正确的实例。
例如,假设我们想要确保实例和实例始终作为一对一起创建。我们可以编写一个自定义管理器类,如下所示:UserProfile

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

现在,这个管理器类可以更好地封装用户实例和配置文件实例始终同时创建。现在可以重写序列化程序类上的方法,以使用新的管理器方法。

1.9 处理多个对象

该类还可以处理对象列表的序列化或反序列化。Serializer

1.9.1 序列化多个对象

若要序列化查询集或对象列表而不是单个对象实例,应在实例化序列化程序时传递该标志。然后,可以传递要序列化的查询集或对象列表。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'}
# ]

1.9.2 反序列化多个对象

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

1.10 包括额外的上下文

在某些情况下,除了要序列化的对象之外,还需要为序列化程序提供额外的上下文。一种常见的情况是,如果使用的序列化程序包含超链接关系,则要求序列化程序有权访问当前请求,以便它可以正确生成完全限定的 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'}

二、模型序列化程序

通常,你需要与 Django 模型定义紧密映射的序列化程序类。
该类提供了一个快捷方式,允许您自动创建具有与 Model 字段对应的字段的类。ModelSerializerSerializer
ModelSerializer 类与常规 Serializer 类相同,不同之处在于

  • 它将根据模型自动为您生成一组字段。

  • 它将自动为序列化程序生成验证器,例如unique_together验证器。

  • 它包括 和 的简单默认实现。.create().update()

声明如下所示:ModelSerializer

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        # 使用Account的模型类
        model = Account
        # 使用Account模型类中指定的字段,all代表所有
        fields = ['id''account_name''users''created']

2.1 指定要包含的字段

如果只想在模型序列化程序中使用默认字段的子集,则可以使用 或 选项执行此操作,可以指定要显示的字段。这样,当模型更改时,不太可能导致无意中暴露数据。

2.1.1 通过fields指定显示的字段

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

2.1.2 使用`all`指定所有fields

还可以将属性设置为特殊值,以指示应使用模型中的所有字段。fields.__all__

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = '__all__'

2.1.3 使用`exclude`排除指定字段

可以将属性设置为要从序列化程序中排除的字段列表。exclude

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        # 排除的字段
        exclude = ['users']

2.2 指定嵌套序列化

默认使用主键来表示关系,但您也可以使用以下选项轻松生成嵌套表示:

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

该选项应设置为一个整数值,该值指示在恢复为平面表示之前应遍历的关系深度。depth
如果要自定义序列化的方式,则需要自行定义字段。

2.3 显式指定字段

您可以通过在类上声明字段来向类添加额外的字段或覆盖默认字段,就像对类所做的那样。

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

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

2.4 指定只读字段

您可能希望将多个字段指定为只读字段。您可以使用快捷键 Meta 选项 read_only=True``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

2.5 其他关键字参数

还有一个快捷方式允许您使用该选项在字段上指定任意附加的关键字参数。与 的情况一样,这意味着您不需要在序列化程序上显式声明该字段。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

请记住,如果该字段已在序列化程序类上显式声明,则该选项将被忽略。


原文始发于微信公众号(Python之家):Django-13-DRF深入序列化器

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/198095.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!