例如,我有以下序列化程序:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = (
'userid',
'password'
)
但我不想在GET上输出密码(当然我的真实例子还有其他字段)。如果不编写其他序列化程序,我怎么能这样做?即时更改字段列表。有没有办法做到这一点?
答案 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_create
和perform_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个序列化程序,如果我知道这些变化是我唯一的变化。否则,请查看示例