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 serializer
user_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