【问题标题】:Updating related objects django rest framework更新相关对象 django rest 框架
【发布时间】:2020-12-24 15:33:28
【问题描述】:

在我的 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() 失败,因为提供的电子邮件已存在于数据库中。这也表明,即使验证通过(即由于更新的电子邮件地址),也会创建一个新的用户对象并将其耦合到配置文件。所以我的问题是:

如何使序列化程序的验证检查嵌套对象的编辑是否有效,而不是检查是否可以创建新对象?

【问题讨论】:

    标签: python django django-rest-framework one-to-one


    【解决方案1】:

    您可以通过执行类似操作来更新配置文件及其相关用户的属性。

    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
    

    【讨论】:

    • 是的,但更新是在 验证之后调用的,对吗?我的部分问题是我没有通过验证,因为帖子数据中提供的电子邮件(如果未更改)由于电子邮件的唯一约束而无效。现在我重新阅读了我的问题,我发现这不是很清楚,对此感到抱歉,将进行编辑。我主要想知道是否有某种方法可以“告诉” django-rest 这个(或所有)关系的默认值是编辑现有关系而不是创建新关系,也用于验证。
    【解决方案2】:

    我设法自己找到了一些解决方法。使用序列化器字段的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 有更多属性,但就我目前的要求而言,我不需要它们,而且这很好用(尽管如果您需要所有属性,为所有属性添加这样的行会很乏味)。它确实需要createupdate 函数的自定义实现,但我认为无论如何都需要这些。我个人使用:

        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 是配置文件属性)

    【讨论】:

      猜你喜欢
      • 2014-07-07
      • 1970-01-01
      • 2015-02-25
      • 1970-01-01
      • 1970-01-01
      • 2020-06-01
      • 1970-01-01
      • 2017-02-04
      • 2014-05-21
      相关资源
      最近更新 更多