序列化器(嵌套对象)

官方文档原文
前面的例子适用于处理只具有简单数据类型的对象,但有时还需要能够表示更复杂的对象,其中对象的某些属性可能不是简单的数据类型,如字符串,日期或整数。
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()



可写嵌套表示
在处理支持反序列化数据的嵌套表示时,嵌套对象的任何错误都将嵌套在嵌套对象的字段名称下。
在 shell 中检验一下:
>>> from myapp.serializer import UserSerializer, CommentSerializer>>> serializer = CommentSerializer(data=https://www.it610.com/article/{'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})>>> serializer.is_valid() False>>> serializer.errors ReturnDict([('user', {'email': ['Enter a valid email address.']}), ('created', ['This field is required.'])])



为嵌套表示书写 .create() 方法
如果你支持可写嵌套表示,则需要编写处理保存多个对象的 .create().update() 方法。
我们有这样的 models,Profile 扩展了用户信息,新增 city 字段保存用户的籍贯,Profile 和 User 用一对一关系关联。
# models.pyfrom django.db import modelsclass Profile(models.Model): city = models.CharField(max_length=50) owner = models.OneToOneField('auth.User', related_name='user_profile')

我们在序列化器体现这种嵌套关系并重写 create() 方法,让创建实例时候同时把关联对象一起创建。
# serializers.pyfrom rest_framework import serializers from myapp.models import Profile from django.contrib.auth.models import Userclass ProfileSerializer(serializers.ModelSerializer): class Meta: model = Profile fields = ('city', 'owner')class UserSerializer(serializers.ModelSerializer): user_profile = ProfileSerializer()class Meta: model = User fields = ('username', 'email', 'user_profile')def create(self, validated_data): # 拿到 profile 相关参数 profile_data = https://www.it610.com/article/validated_data.pop('profile') # 创建 user 实例 user = User.objects.create(**validated_data) # 创建 profile 实例 Profile.objects.create(owner=user, **profile_data)return user

在 shell 中我们能这样创建一个 user 以及和它关联的 profile 对象:
>>> from myapp.serializer import UserSerializer>>> validated_data = https://www.it610.com/article/{'username': 'user2', 'email': 'user2@django.com', 'profile': {'city': 'xiamen'} }>>> us = UserSerializer() >>> us.create(validated_data = https://www.it610.com/article/validated_data)



为嵌套关系定义.update() 方法
对于更新,你需要仔细考虑如何处理关联字段的更新。 例如,如果关联字段的值是 None,或者没有提供,那么会发生下面哪一项?
  • 在数据库中将关联字段设置成 NULL
  • 删除关联的实例。
  • 忽略数据并保留这个实例。
  • 抛出验证错误。
以下是我们以前的 UserSerializer 类中的 .update() 方法的示例。
# serializers.pydef update(self, instance, validated_data): profile_data = https://www.it610.com/article/validated_data.pop('profile')profile = instance.user_profileinstance.username = validated_data.get('username', instance.username) instance.email = validated_data.get('email', instance.email) instance.save()profile.city = profile_data.get('city', profile.city) profile.save()return instance

在 shell 中演示修改一个 User 实例:
>>> from myapp.models import Profile >>> from myapp.serializer import UserSerializer>>> validated_data = https://www.it610.com/article/{'email': 'new@django.com', 'profile': {'city': 'beijing'} }>>> us = UserSerializer() >>> u = User.objects.get(pk=2) >>> us.update(instance = u, validated_data = https://www.it610.com/article/validated_data)

因为嵌套创建和更新的行为可能不明确,并且可能需要相关模型之间的复杂依赖关系,所以 REST framework 要求你始终明确写入这些方法。默认的 ModelSerializer.create().update() 方法不包括对可写嵌套表示的支持。


在模型管理器类中保存相关的实例
在序列化类中保存多个相关实例的另一种方法是编写自定义模型管理器类。
例如,假设我们希望确保 User 实例和 Profile 实例始终作为一对创建。我们可能会编写一个类似下面的自定义管理器类:
# models.pyfrom django.db import models from django.contrib.auth.models import Userclass Profile(models.Model): city = models.CharField(max_length=50) owner = models.OneToOneField('auth.User', related_name='user_profile')class UserManager(models.Manager): # 重写 create() 方法 def create(self, username, email, city): user = User(username=username, email=email) user.save()profile = Profile(owner=user, city=city) profile.save()return user# 指定 User 使用该管理器 User.objects = UserManager()

此管理器类现在更好地封装了 User 实例和 profile 实例,使它们始终在同一时间创建。
我们可以方便地用这种方法创建 User 和他关联的 profile 对象:
>>> User.objects.create(username='user3', email='user3@django.com', city='beijing')

现在可以重新编写序列化类上的 .create() 方法,以使用新的管理类方法。
# serializers.pydef create(self, validated_data): user =User.objects.create( username = validated_data['username'], email = validated_data['email'], city = validated_data['profile']['city'] ) return user

【序列化器(嵌套对象)】在 shell 中演示创建一个 User 实例:
>>> from myapp.serializer import UserSerializer >>> us = UserSerializer() >>> validated_data = https://www.it610.com/article/{'username': 'user4', 'email': 'user4@django.com', 'profile': {'city': 'hangzhou'} } >>> us.create(validated_data)

    推荐阅读