序列化器:嵌套对象

2018-05-04  本文已影响421人  SingleDiego

官方文档原文

前面的例子适用于处理只具有简单数据类型的对象,但有时还需要能够表示更复杂的对象,其中对象的某些属性可能不是简单的数据类型,如字符串,日期或整数。

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={'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.py

from django.db import models

class Profile(models.Model):
    city = models.CharField(max_length=50)
    owner = models.OneToOneField('auth.User', related_name='user_profile')

我们在序列化器体现这种嵌套关系并重写 create() 方法,让创建实例时候同时把关联对象一起创建。

# serializers.py

from rest_framework import serializers
from myapp.models import Profile
from django.contrib.auth.models import User


class 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 = 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 = {  
    'username': 'user2', 
    'email': 'user2@django.com', 
    'profile': {'city': 'xiamen'}
}

>>> us = UserSerializer()
>>> us.create(validated_data = validated_data)




为嵌套关系定义.update() 方法

对于更新,你需要仔细考虑如何处理关联字段的更新。 例如,如果关联字段的值是 None,或者没有提供,那么会发生下面哪一项?

以下是我们以前的 UserSerializer 类中的 .update() 方法的示例。

# serializers.py

def update(self, instance, validated_data):
    profile_data = validated_data.pop('profile')

    profile = instance.user_profile

    instance.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 = {  
    'email': 'new@django.com', 
    'profile': {'city': 'beijing'}
}

>>> us = UserSerializer()
>>> u = User.objects.get(pk=2) 
>>> us.update(instance = u, validated_data = validated_data)

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




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

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

例如,假设我们希望确保 User 实例和 Profile 实例始终作为一对创建。我们可能会编写一个类似下面的自定义管理器类:

# models.py

from django.db import models
from django.contrib.auth.models import User

class 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.py

def 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 = {
    'username': 'user4', 
    'email': 'user4@django.com', 
    'profile': {'city': 'hangzhou'}
}
>>> us.create(validated_data)
上一篇下一篇

猜你喜欢

热点阅读