Reputation: 169
Serializer:
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ('foo', 'bar')
class UserSerializer(serializers.ModelSerializer):
userprofile = ProfileSerializer(partial=True)
class Meta:
model = User
fields = ('username', 'password', 'email', 'userprofile')
def create(self, validated_data):
profile_data = validated_data.pop('userprofile')
user = User.objects.create(**validated_data)
UserProfile.objects.create(user=user, **profile_data)
return user
def update(self, instance, validated_data):
profile_data = validated_data.pop('userprofile')
profile = instance.userprofile
instance.username = validated_data.get('username', instance.username)
instance.email = validated_data.get('email', instance.email)
instance.save()
profile.foo = profile_data.get('foo', profile.foo)
profile.bar = profile_data.get('bar', profile.bar)
profile.save()
return instance
View:
class UsersViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,)
Both create and update are working just fine, problem is with partial updates. The django User model has as required username and I would like to make that optional. Is there a way to enable partial updates for this scenario?
For instance I would like to update with PUT just "foo".
Upvotes: 7
Views: 7799
Reputation: 1
I think it is enough to enforce partial=True
when you define and override the update()
method in serializers.py, i.e.:
def update(self, instance, validated_data, partial=True):
...
Upvotes: 0
Reputation: 169
I ended up overriding get_serializer inside UsersViewSet:
def get_serializer(self, instance=None, data=None, many=False, partial=False):
"""If request is not PUT, allow partial updates."""
if self.request.method == 'PUT':
return UserSerializer(instance=instance, data=data, many=many, partial=True)
else:
return UserSerializer(instance=instance, data=data, many=many, partial=partial)
Forcing partial to True if request.method is PUT. Not sure if this is the most elegant solution but it works. If any one has a better solution please do share :)
Upvotes: 4
Reputation: 917
Actually, the following code already supports the partial update:
instance.username = validated_data.get('username', instance.username)
This 'get' function will get the 'username' field from validated_data. If it didn't exist, then instance.username would be returned.
Upvotes: 1
Reputation: 2580
By default PUT is expected to supply all required arguments. But PATCH is not. So as long as you are OK with using PATCH instead of PUT you don't have to change your code at all.
Personally I think it is a little weird how that works, PUT requires all arguments that aren't optional, but will leave optional arguments alone. So you can edit optional arguments while leaving other optional arguments alone but can't do the same with required arguments (I mean you can obviously just supply the existing values, but if they changed whilst you were editing you are screwed and will force change them back). PATCH makes a lot more sense to me. Any argument you supply will be changed, and any you don't won't. IMO PUT should wipe out any optional arguments that aren't supplied, so that it is a true replace rather than simply a replace required and update (PUT) optional.
Upvotes: 6
Reputation: 8006
The user seriliazer needs to change to something like
username = fields.CharField(required=False)
Upvotes: 0