Single Update and Delete API for two models connected with a OneToOne relationship in Django Rest Framework

I've looked extensively on here and probably exhausted all the answers and still haven't found a solution to my particular problem, which is to make an API that update/delete from both models, and I am getting the following error:

The .update()method does not support writable nested fields by default. Write an explicit.update()method for serializeruser_profile.serializers.UserSerializer, or set read_only=True on nested serializer fields.

In this particular instance this happens when I try to update a field from the user_profile model

I have separated my Django project into several apps/folders with each model being in its own folder.

I have a user app and a user_profile app each with their own models.

the user model is basically an AbstractUser sitting in its own app

the user_profile model is as follows:

class UserProfile(models.Model): user = models.OneToOneField(to=User, on_delete=models.CASCADE, related_name='userprofile') location = models.CharField(blank=True, max_length=30) created_time = models.DateTimeField(auto_now_add=True) updated_time = models.DateTimeField(auto_now=True)

The serializers are as follows:

`class UserProfileCrudSerializer(serializers.ModelSerializer): class Meta: model = UserProfile fields = ('location', 'created_time', 'updated_time')

class UserSerializer(serializers.ModelSerializer): profile = UserProfileCrudSerializer(source='userprofile', many=False)

class Meta:
    model = User
    fields = ('username', 'email', 'first_name', 'last_name', 'profile')

    def update(self, instance, validated_data):
        userprofile_serializer = self.fields['profile']
        userprofile_instance = instance.userprofile
        userprofile_data = validated_data.pop('userprofile', {})

        userprofile_serializer.update(userprofile_instance, userprofile_data)

        instance = super().update(instance, validated_data)
        return instance`

and my view is:

`class RetrieveUpdateView(RetrieveUpdateAPIView): serializer_class = UserSerializer queryset = User.objects.all()

def get_object(self):
    return self.request.user`

when I do a GET I am getting the following response without any problems:

{ "username": "blue", "email": "bluebear@bluebear.com", "first_name": "Blue", "last_name": "Bear", "profile": { "location": "London", "created_time": "2023-02-03T00:39:15.149924Z", "updated_time": "2023-02-03T00:39:15.149924Z" } }

and I do a patch request like this:

{ "profile": { "location": "Paris" } }

The way the code is now I have no issue updating username, email, first_name, and last_name which come from the AbstractUser but I am getting the above error when I try to patch the location which is in the UserProfile model.

I've looked at many similar solutions online, but none that pertain to my particular situation.

The .update() method does not support writable nested fields by default. Write an explicit .update() method for serializeruser_profile.serializers.UserSerializer, or set read_only=True on nested serializer fields.

It already shows in the message, you need to explicitly write the update method for the writable nested serializer which is documented here https://www.django-rest-framework.org/topics/writable-nested-serializers/ or you can use another module that is also referred to in the docs https://github.com/beda-software/drf-writable-nested.

Your approach is correct already but there is some typo and wrong indentation in your code:

class UserSerializer(serializers.ModelSerializer):
    profile = UserProfileCrudSerializer(source='userprofile', many=False)

    class Meta:
        model = User
        fields = ('username', 'email', 'first_name', 'last_name', 'profile')

    def update(self, instance, validated_data):
        # update is a method of serializer not serializer.Meta
        userprofile_serializer = self.fields['profile']
        userprofile_instance = instance.userprofile
        # should be 'profile' here instead of 'userprofile' as you defined in serializer
        userprofile_data = validated_data.pop('profile', {})

        userprofile_serializer.update(userprofile_instance, userprofile_data)

        instance = super().update(instance, validated_data)
        return instance
Back to Top