Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django rest framework nested serializer partial update

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".

like image 471
zerg . Avatar asked Dec 18 '14 18:12

zerg .


2 Answers

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.

like image 82
semicolon Avatar answered Sep 29 '22 13:09

semicolon


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 :)

like image 21
zerg . Avatar answered Sep 29 '22 11:09

zerg .