drf-writable-nested
drf-writable-nested copied to clipboard
Nested Serializer with UniqueFieldsMixin runs '.create()' in nested serializer instead of '.update()'
When calling .save(instance, data, partial) from an outer serializer with NestedCreateMixin and NestedUpdateMixin, nested serializer wih UniqueFieldsMixin runs .create() instead of .update() thus resulting IntegritiyError being raised.
I have this outer serializer with nested UserSerializer:
class ProfileSerializer(NestedCreateMixin, NestedUpdateMixin, serializers.ModelSerializer):
class Meta:
model = Profile
fields = ['user', 'address', 'cart_books']
address = serializers.CharField(required=True)
user = UserSerializer()
cart_books = BookSerializer(many=True, required=False)
and this is UserSerializer:
class UserSerializer(UniqueFieldsMixin, serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'first_name', 'last_name', 'is_staff',
'email', 'password', 'last_login', 'date_joined']
extra_kwargs = {
'password': {'write_only': True},
'is_staff': {'write_only': True},
'date_joined': {'read_only': True},
'last_login': {'read_only': True},
}
def create(self, validated_data):
password = validated_data.pop('password', None)
instance = self.Meta.model(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
def update(self, instance, validated_data):
for attr, value in validated_data.items():
if attr == 'password':
instance.set_password(value)
else:
setattr(instance, attr, value)
instance.save()
return instance
when I tried to run this, I got IntegrityError:
user = User.objects.get(id=pk)
if user != request.user:
raise PermissionDenied()
partial = kwargs.get('partial', False)
profile_data = {
'address': request.data.pop('address', None)
}
profile_data['user'] = request.data
profile = Profile.objects.get(user=user)
profile_serializer = ProfileSerializer(instance=profile, data=profile_data, partial=partial)
if profile_serializer.is_valid(raise_exception=True):
profile_serializer.save()
return Response({
'message': 'Updating user successful.',
'data': profile_serializer.data
}, status=200)
Is there something I was missing? Or is this the expected behaviour?
Did you solve this problem? I'm also trying to add multiple objects to a M2M field, but it seems that calling save() on a partial update tries to first create the objects even though they already exist.
Did you solve this problem? I'm also trying to add multiple objects to a M2M field, but it seems that calling save() on a partial update tries to first create the objects even though they already exist.
Hi, sorry for late reply.
I ended up overriding the update
method in the outer serializer, creating the nested serializer instance inside it with the validated_data
. Like so:
def update(self, instance, validated_data, *args, **kwargs):
user_data = validated_data.pop('user')
user_serializer = UserSerializer(instance.user, data=user_data, partial=self.partial)
if user_serializer.is_valid():
user = user_serializer.save()
for key, value in validated_data.items():
setattr(instance, key, value)
instance.user = user
instance.save()
return instance