I'm trying to make my User model RESTful via Django Rest Framework API calls, so that I can create users as well as update their profiles.
However, as I go through a particular verification process with my users, I do not want the users to have the ability to update the username after their account is created. I attempted to use read_only_fields, but that seemed to disable that field in POST operations, so I was unable to specify a username when creating the user object.
How can I go about implementing this? Relevant code for the API as it exists now is below.
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()]
Thanks!
UPDATE:
Turns out Rest Framework already comes equipped with this functionality. The correct way of having a "create-only" field is by using the
CreateOnlyDefault()
option.I guess the only thing left to say is Read the Docs!!! http://www.django-rest-framework.org/api-guide/validators/#createonlydefault
Old Answer:
Looks I'm quite late to the party but here are my two cents anyway.
To me it doesn't make sense to have two different serializers just because you want to prevent a field from being updated. I had this exact same issue and the approach I used was to implement my own
validate
method in the Serializer class. In my case, the field I don't want updated is calledowner
. Here is the relevant code:And here is a unit test to validate it:
I raise a
ValidationError
because I don't want to hide the fact that someone tried to perform an invalid operation. If you don't want to do this and you want to allow the operation to be completed without updating the field instead, do the following:remove the line:
and replace it with:
Hope this helps!
My approach is to modify the
perform_update
method when using generics view classes. I remove the field when update is performed.If you don't want to create another serializer, you may want to try customizing
get_serializer_class()
insideMyViewSet
. This has been useful to me for simple projects.This post mentions four different ways to achieve this goal.
This was the cleanest way I think: [collection must not be edited]
Another method would be to add a validation method, but throw a validation error if the instance already exists and the value has changed:
In my case, I wanted a foreign key to never be updated:
See also: Level-field validation in django rest framework 3.1 - access to the old value