Django Rest Framework:在创建对象后禁用字段更新

Bra*_*don 58 django rest django-rest-framework

我正在尝试通过Django Rest Framework API调用使我的用户模型RESTful,以便我可以创建用户以及更新他们的配置文件.

但是,当我与用户进行特定的验证过程时,我不希望用户在创建帐户后能够更新用户名.我试图使用read_only_fields,但这似乎在POST操作中禁用了该字段,因此在创建用户对象时我无法指定用户名.

我该如何实现呢?现在存在的API的相关代码如下.

class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ('url', 'username', 'password', 'email')
        write_only_fields = ('password',)

    def restore_object(self, attrs, instance=None):
        user = super(UserSerializer, self).restore_object(attrs, instance)
        user.set_password(attrs['password'])
        return user


class UserViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows users to be viewed or edited.
    """
    serializer_class = UserSerializer
    model = User

    def get_permissions(self):
        if self.request.method == 'DELETE':
            return [IsAdminUser()]
        elif self.request.method == 'POST':
            return [AllowAny()]
        else:
            return [IsStaffOrTargetUser()]
Run Code Online (Sandbox Code Playgroud)

谢谢!

And*_*dov 53

看来你需要为POST和PUT方法使用不同的序列化器.在PUT方法的序列化器中,您只能使用用户名字段(或将用户名字段设置为只读).

class UserViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows users to be viewed or edited.
    """
    serializer_class = UserSerializer
    model = User

    def get_serializer_class(self):
        serializer_class = self.serializer_class

        if self.request.method == 'PUT':
            serializer_class = SerializerWithoutUsernameField

        return serializer_class

    def get_permissions(self):
        if self.request.method == 'DELETE':
            return [IsAdminUser()]
        elif self.request.method == 'POST':
            return [AllowAny()]
        else:
            return [IsStaffOrTargetUser()]
Run Code Online (Sandbox Code Playgroud)

检查这个问题django-rest-framework:在相同的URL中使用不同的泛型视图的独立GET和PUT

  • 由于大型序列化器的代码重复,这很快就会变得难以管理。 (5认同)

小智 23

另一种选择(仅限DRF3)

class MySerializer(serializers.ModelSerializer):
    ...
    def get_extra_kwargs(self):
        extra_kwargs = super(MySerializer, self).get_extra_kwargs()
        action = self.context['view'].action

        if action in ['create']:
            kwargs = extra_kwargs.get('ro_oncreate_field', {})
            kwargs['read_only'] = True
            extra_kwargs['ro_oncreate_field'] = kwargs

        elif action in ['update', 'partial_update']:
            kwargs = extra_kwargs.get('ro_onupdate_field', {})
            kwargs['read_only'] = True
            extra_kwargs['ro_onupdate_field'] = kwargs

        return extra_kwargs
Run Code Online (Sandbox Code Playgroud)

  • 好方法.一个小建议,我会使用`if self.instance是None:`而不是view.action check.这允许您在标准drf视图之外使用序列化程序.即我有一个场景,我在从外部csv文件导入数据时在芹菜任务中使用序列化器.在这种情况下,view属性将为null,但self.instance检查仍然有效. (11认同)

rra*_*nza 8

另一种方法是添加验证方法,但如果实例已存在且值已更改,则抛出验证错误:

def validate_foo(self, value):                                     
    if self.instance and value != self.instance.foo:
        raise serializers.ValidationError("foo is immutable once set.")
    return value         
Run Code Online (Sandbox Code Playgroud)

就我而言,我希望永远不会更新外键:

def validate_foo_id(self, value):                                     
    if self.instance and value.id != self.instance.foo_id:            
        raise serializers.ValidationError("foo_id is immutable once set.")
    return value         
Run Code Online (Sandbox Code Playgroud)

另请参阅:django rest framework 3.1 中的级别字段验证 - 访问旧值

  • 我同意用户应该知道为什么该字段无法更新,这是完成这项工作的唯一答案。与霍贾特的回答不同。在这里,您仍然可以重新发送相同的序列化对象,而不会拒绝请求。 (2认同)

Goo*_*han 5

我的方法是perform_update在使用泛型视图类时修改方法。我在执行更新时删除该字段。

class UpdateView(generics.UpdateAPIView):
    ...
    def perform_update(self, serializer):
        #remove some field
        rem_field = serializer.validated_data.pop('some_field', None)
        serializer.save()
Run Code Online (Sandbox Code Playgroud)