更新相关对象 django rest framework
Updating related objects django rest framework
在我的 django 项目中,我有 2 个相关模型:
class User(AbstractUser):
email = models.EmailField(
verbose_name='e-mailaddress',
max_length=255,
unique=True)
# other props not important
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
#other props not important
对于创建和检索,在序列化程序中将 User 作为嵌套对象很方便:
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Profile
fields = '__all__'
def create(self, validated_data):
user_data = validated_data.pop('user')
user = User.objects.create(**user_data)
user.save()
# because of reasons, an empty profile is created on initial save. This works, trust me
d, _ = Profile.objects.update_or_create(user=user, defaults=validated_data)
d.save()
return d
然而。我也希望能够执行的一项操作是同时更新配置文件和用户的属性。使用我的序列化器执行此操作时,serializer.is_valid()
失败,因为提供的电子邮件已存在于数据库中。这也表明,即使验证通过(即由于更新的电子邮件地址),也将创建一个新的用户对象并将其耦合到配置文件。所以我的问题是:
如何使序列化程序的验证检查嵌套对象的 edit 是否有效,而不是检查是否可以创建新对象?
您可以通过执行类似的操作来更新个人资料及其相关用户的属性。
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Profile
fields = '__all__'
def update(self, instance, validated_data):
# update the profile-related fields here.
# the other properties which you didn't mention.
...
profile = instance
# get the profile object
...
instance.save()
user = validated_data.get('user')
# get the related user.
# update the user properties here, like:
user.email = 'UPDATED_EMAIL'
user.save() # you need to save the user as well as the profile!
return instance
我设法自己找到了一些解决方法。使用序列化器字段的 source
属性,可以将相关对象的某些属性添加到序列化器。就我而言,我能够将以下行添加到 ProfileSerializer 以获得我需要的功能:
first_name = serializers.CharField(max_length=30, source='user.first_name', required=False)
last_name = serializers.CharField(max_length=150, source='user.last_name', required=False)
email = serializers.CharField(max_length=255, source='user.email')
User
还有更多的属性,但对于我目前的要求,我不需要它们并且这工作正常(尽管如果您需要所有属性,为所有属性添加这样一行会很乏味).它确实需要 create
和 update
函数的自定义实现,但我认为无论如何都会需要它们。我个人使用:
def create(self, validated_data):
user_data = validated_data.pop('user')
user = User.objects.create(**user_data)
user.save()
instance = ...
return instance
def update(self, instance, validated_data):
user_data = validated_data.pop('user')
user = instance.user
for k,v in user_data.items():
setattr(user, k, v)
user.save()
...
return instance
注意:django-rest 自动将具有共享源对象的属性分组到嵌套对象中:{'user': {'email': ..., 'first_name': ..., 'last_name': ...}, 'phone_number': ...}
(其中 phone_number
是配置文件 属性)
在我的 django 项目中,我有 2 个相关模型:
class User(AbstractUser):
email = models.EmailField(
verbose_name='e-mailaddress',
max_length=255,
unique=True)
# other props not important
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
#other props not important
对于创建和检索,在序列化程序中将 User 作为嵌套对象很方便:
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Profile
fields = '__all__'
def create(self, validated_data):
user_data = validated_data.pop('user')
user = User.objects.create(**user_data)
user.save()
# because of reasons, an empty profile is created on initial save. This works, trust me
d, _ = Profile.objects.update_or_create(user=user, defaults=validated_data)
d.save()
return d
然而。我也希望能够执行的一项操作是同时更新配置文件和用户的属性。使用我的序列化器执行此操作时,serializer.is_valid()
失败,因为提供的电子邮件已存在于数据库中。这也表明,即使验证通过(即由于更新的电子邮件地址),也将创建一个新的用户对象并将其耦合到配置文件。所以我的问题是:
如何使序列化程序的验证检查嵌套对象的 edit 是否有效,而不是检查是否可以创建新对象?
您可以通过执行类似的操作来更新个人资料及其相关用户的属性。
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Profile
fields = '__all__'
def update(self, instance, validated_data):
# update the profile-related fields here.
# the other properties which you didn't mention.
...
profile = instance
# get the profile object
...
instance.save()
user = validated_data.get('user')
# get the related user.
# update the user properties here, like:
user.email = 'UPDATED_EMAIL'
user.save() # you need to save the user as well as the profile!
return instance
我设法自己找到了一些解决方法。使用序列化器字段的 source
属性,可以将相关对象的某些属性添加到序列化器。就我而言,我能够将以下行添加到 ProfileSerializer 以获得我需要的功能:
first_name = serializers.CharField(max_length=30, source='user.first_name', required=False)
last_name = serializers.CharField(max_length=150, source='user.last_name', required=False)
email = serializers.CharField(max_length=255, source='user.email')
User
还有更多的属性,但对于我目前的要求,我不需要它们并且这工作正常(尽管如果您需要所有属性,为所有属性添加这样一行会很乏味).它确实需要 create
和 update
函数的自定义实现,但我认为无论如何都会需要它们。我个人使用:
def create(self, validated_data):
user_data = validated_data.pop('user')
user = User.objects.create(**user_data)
user.save()
instance = ...
return instance
def update(self, instance, validated_data):
user_data = validated_data.pop('user')
user = instance.user
for k,v in user_data.items():
setattr(user, k, v)
user.save()
...
return instance
注意:django-rest 自动将具有共享源对象的属性分组到嵌套对象中:{'user': {'email': ..., 'first_name': ..., 'last_name': ...}, 'phone_number': ...}
(其中 phone_number
是配置文件 属性)