Django Rest Framework:创build对象后禁用字段更新
我试图通过Django Rest Framework API调用使我的用户模型RESTful,以便我可以创build用户以及更新他们的configuration文件。
但是,当我与用户进行特定的validation过程时,我不希望用户能够在帐户创build后更新用户名。 我试图使用read_only_fields,但似乎在POST操作中禁用了该字段,所以在创build用户对象时我无法指定用户名。
我怎么去执行这个? 现在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()]
谢谢!
看来你需要不同的序列化程序的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()]
检查这个问题django-rest-framework:独立的GET和PUT在同一个URL但不同的generics视图
另一个选项(仅限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
更新:
原来Rest Framework已经配备了这个function。 具有“仅创build”字段的正确方法是使用CreateOnlyDefault()
选项。
我想唯一剩下要说的是阅读文档! http://www.django-rest-framework.org/api-guide/validators/#createonlydefault
老答案:
看起来我晚了很晚,但这里是我的两分钱。
对我而言,仅仅因为想阻止更新字段而使用两个不同的序列化器是没有意义的。 我有这个完全相同的问题,我用的方法是在Serializer类中实现我自己的validate
方法。 在我的情况下,我不想更新的字段被称为owner
。 这是相关的代码:
class BusinessSerializer(serializers.ModelSerializer): class Meta: model = Business pass def validate(self, data): instance = self.instance # this means it's an update # see also: http://www.django-rest-framework.org/api-guide/serializers/#accessing-the-initial-data-and-instance if instance is not None: originalOwner = instance.owner # if 'dataOwner' is not None it means they're trying to update the owner field dataOwner = data.get('owner') if dataOwner is not None and (originalOwner != dataOwner): raise ValidationError('Cannot update owner') return data pass pass
这是一个unit testing来validation它:
def test_owner_cant_be_updated(self): harry = User.objects.get(username='harry') jack = User.objects.get(username='jack') # create object serializer = BusinessSerializer(data={'name': 'My Company', 'owner': harry.id}) self.assertTrue(serializer.is_valid()) serializer.save() # retrieve object business = Business.objects.get(name='My Company') self.assertIsNotNone(business) # update object serializer = BusinessSerializer(business, data={'owner': jack.id}, partial=True) # this will be False! owners cannot be updated! self.assertFalse(serializer.is_valid()) pass
我提出了一个ValidationError
因为我不想隐藏有人试图执行无效操作的事实。 如果您不想执行此操作,并且您希望允许在不更新字段的情况下完成操作,请执行以下操作:
删除该行:
raise ValidationError('Cannot update owner')
并将其replace为:
data.update({'owner': originalOwner})
希望这可以帮助!
另一个解决scheme(除了创build一个单独的序列化程序),如果设置了实例(这意味着它是一个PATCH / PUT方法),将从restore_object方法中的attrspopup用户名:
def restore_object(self, attrs, instance=None): if instance is not None: attrs.pop('username', None) user = super(UserSerializer, self).restore_object(attrs, instance) user.set_password(attrs['password']) return user
我使用这种方法:
def get_serializer_class(self): if getattr(self, 'object', None) is None: return super(UserViewSet, self).get_serializer_class() else: return SerializerWithoutUsernameField
我的方法是在使用generics视图类时修改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()
如果你不想创build另一个序列化器,你可能想尝试在MyViewSet
定制get_serializer_class()
。 这对我来说简单的项目是有用的。
# Your clean serializer class MySerializer(serializers.ModelSerializer): class Meta: model = MyModel fields = '__all__' # Your hardworking viewset class MyViewSet(MyParentViewSet): serializer_class = MySerializer model = MyModel def get_serializer_class(self): serializer_class = self.serializer_class if self.request.method in ['PUT', 'PATCH']: # setting `exclude` while having `fields` raises an error # so set `read_only_fields` if request is PUT/PATCH setattr(serializer_class.Meta, 'read_only_fields', ('non_updatable_field',)) # set serializer_class here instead if you have another serializer for finer control return serializer_class
setattr(对象,名称,值)
这是getattr()的对应部分。 参数是一个对象,一个string和一个任意值。 该string可以命名现有的属性或新的属性。 该函数将该值赋给该属性,只要该对象允许。 例如,setattr(x,'foobar',123)等同于x.foobar = 123。