使用RetrieveUpdateAPIView更新数据 - 从序列化程序获取经过验证的数据

时间:2018-05-10 00:20:29

标签: django django-rest-framework

我想更新用户的某些属性(比如first_name和last_name) 我的json对象通过PUT请求看起来像这样

   {
"user" :    {   
             "first_name": "Jack",   
             "last_name": "shnider",
             "password":"admin123" 
             "email" : "foo@google.com"
             },

"employee_zip" : 12345
}

这就是我的观点(我想将现有字段更新为这些新字段)。

这些是序列化程序

  class Serializer_UpdateUser(ModelSerializer):
    class Meta:
        model = User
        fields = ('first_name','last_name','password')

class Serializer_UpdateEmployer(ModelSerializer):
    user = Serializer_UpdateUser()
    class Meta:
        model = modelEmployer
        fields = [
            'user',
            'employer_zip',
        ]

这是观点:

class UpdateProfile_RetrieveUpdateAPIView(RetrieveUpdateAPIView):
    queryset = modelEmployer.objects.all()
    serializer_class = Serializer_UpdateEmployer
    lookup_field = 'user__email'
    permission_classes = [permissions.AllowAny]

    def update(self, request, *args, **kwargs):
        instance = self.get_object() #------>I have the object that I would like to update
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True) #--->Success

现在我想获得一个经过验证的字段(json只包含已更新的字段)。我知道我是否做过这样的事情

serializer.save

我会找回一个modelEmployer,但我会收回此错误

AssertionError at /api/employer/update_profile/employerA@gmail.com/ The `.update()` method does not support writable nested fields by default. Write an explicit `.update()` method for serializer `Employer.api.serializers.Serializer_ListEmployer`, or set `read_only=True` on nested serializer fields. Request Method: 

我有两个问题

1 - 为什么保存失败?

2 - 如何从上面的序列化器中获取经过验证的数据?

2 个答案:

答案 0 :(得分:1)

保存失败,因为默认情况下django-rest-framework不处理嵌套序列化程序。

来自django-rest-framework docs

  

默认情况下,嵌套序列化程序是   只读。如果要支持嵌套的写操作   你需要创建create()和/或update()的序列化器字段   方法以明确指定子关系如何   应该保存。

您必须覆盖序列化程序中的update方法以允许该行为:

class Serializer_UpdateEmployer(ModelSerializer):
    user = Serializer_UpdateUser()
    class Meta:
        model = modelEmployer
        fields = [
            'user',
            'employer_zip',
        ]

    def update(self, instance, validated_data):
        user_data = validated_data.pop('user', {})
        user_serializer = Serializer_UpdateUser(instance.user, data=user_data)
        user_serializer.save()
        return instance

另一种解决方案是使用drf-writable-nested。它会自动使嵌套的序列化程序可更新。

from drf_writable_nested import WritableNestedModelSerializer

class Serializer_UpdateEmployer(WritableNestedModelSerializer):
    user = Serializer_UpdateUser()
    class Meta:
        model = modelEmployer
        fields = [
            'user',
            'employer_zip',
        ]

答案 1 :(得分:1)

我认为drf-writable-nested可以帮助您更新嵌套数据。

在你的情况下:

from django.contrib.auth import password_validation

  class Serializer_UpdateUser(ModelSerializer):

    def update(self, instance, validated_data):
        password = validated_data.pop('password', None)
        super(Serializer_UpdateUser, self).update(instance, validated_data)
        if password is not None:
            instance.set_password(password)
            instance.save()
        return instance

    def validate_password(self, value):
        password_validation.validate_password(value)
        return value

    class Meta:
        model = User
        fields = ('first_name','last_name','password')

class Serializer_UpdateEmployer(WritableNestedModelSerializer):
    user = Serializer_UpdateUser()
    class Meta:
        model = modelEmployer
        fields = [
            'user',
            'employer_zip',
        ]

请注意,您需要特殊处理password字段。