即时更改序列化程序字段

时间:2014-12-14 10:49:31

标签: django serialization django-rest-framework

例如,我有以下序列化程序:

class UserSerializer(serializers.ModelSerializer):

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

但我不想在GET上输出密码(当然我的真实例子还有其他字段)。如果不编写其他序列化程序,我怎么能这样做?即时更改字段列表。有没有办法做到这一点?

4 个答案:

答案 0 :(得分:21)

您似乎在寻找只写字段。因此,在创建时将需要该字段,但它根本不会显示给用户(与只读字段相反)。幸运的是,Django REST Framework现在支持只有the write_only attribute的只写字段。

在Django REST Framework 3.0中,您只需要添加额外的参数to the extra_kwargs meta option

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = (
            'userid',
            'password'
        )
        extra_kwargs = {
            'password': {
                'write_only': True,
            },
        }

因为password应该被哈希(你正在使用Django的用户,对吗?),你需要在密码进入时对其进行哈希处理。这应该在你的视图上完成,很可能是通过覆盖perform_createperform_update方法。

from django.contrib.auth.hashers import make_password

class UserViewSet(viewsets.ViewSet):

    def perform_create(self, serializer):
        password = make_password(self.request.data['password'])

        serializer.save(password=password)

    def perform_update(self, serializer):
        password = make_password(self.request.data['password'])

        serializer.save(password=password)

在Django REST Framework 2.x 中,您需要完全重新定义序列化程序上的password字段。

class UserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True)

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

为了在Django REST Framework 2.x中提前散列密码,您需要覆盖pre_save

from django.contrib.auth.hashers import make_password

class UserViewSet(viewsets.ViewSet):

    def pre_save(self, obj, created=False):
        obj.password = make_password(obj.password)

        super(UserViewSet, self).pre_save(obj, created=created)

这将解决其他答案的常见问题,即用于创建/更新用户的相同序列化程序也将用于返回更新的用户对象作为响应。这意味着即使您只希望密码是只写的,密码仍会在响应中返回。此问题的另一个问题是密码可能会或可能不会在响应中进行哈希处理,这是您真正不想做的事情。

答案 1 :(得分:2)

@Kevin Brown的解决方案还有一件事。

由于partial update也会执行perform_update,因此最好添加额外的代码,如下所示。

def perform_update(self, serializer):                                                           
    if 'password' in self.request.data:
        password = make_password(self.request.data['password'])                                 
        serializer.save(password=password)                                                      
    else:
        serializer.save()

答案 2 :(得分:1)

这应该是你需要的。我使用了一个函数视图,但如果您愿意,可以使用View View或ViewSet(覆盖get_serializer_class)。

请注意,serializer_factory也接受exclude=,但出于安全原因,我更喜欢使用fields=

serializer_factory使用现有的Serializer作为基础创建一个Serializer类(与django modelform_factory相同)

==============

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = (
           'userid',
           'password'
        )


@api_view(['GET', 'POST'])
def user_list(request):
    User = get_user_model()

    if request.method == 'GET':
        fields=['userid']
    elif request.method == 'POST':
        fields = None
    serializer = serializer_factory(User, UserSerializer, fields=fields)
    return Response(serializer.data)

def serializer_factory(model, base=HyperlinkedModelSerializer,
                       fields=None, exclude=None):
    attrs = {'model': model}
    if fields is not None:
        attrs['fields'] = fields
    if exclude is not None:
        attrs['exclude'] = exclude

    parent = (object,)
    if hasattr(base, 'Meta'):
        parent = (base.Meta, object)
    Meta = type(str('Meta'), parent, attrs)
    if model:
        class_name = model.__name__ + 'Serializer'
    else:
        class_name = 'Serializer'
    return type(base)(class_name, (base,), {'Meta': Meta, })

答案 3 :(得分:0)

据我从文档中可以看出,最快的方法是根据您的观点有条件地设置2个序列化器。

此外,文档显示了另一种选择,但它有点过分: http://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields

它涉及创建智能初始化方法,举例说明。我只使用2个序列化程序,如果我知道这些变化是我唯一的变化。否则,请查看示例

相关问题