前面的例子适用于处理只具有简单数据类型的对象,但有时还需要能够表示更复杂的对象,其中对象的某些属性可能不是简单的数据类型,如字符串,日期或整数。
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
,或者没有提供,那么会发生下面哪一项?
- 在数据库中将关联字段设置成
NULL
。 - 删除关联的实例。
- 忽略数据并保留这个实例。
- 抛出验证错误。
以下是我们以前的 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)
网友评论