Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nested Serializer with UniqueFieldsMixin runs '.create()' in nested serializer instead of '.update()' #134

Open
prasetyaputraa opened this issue Jan 27, 2021 · 2 comments

Comments

@prasetyaputraa
Copy link

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?

@MatejMijoski
Copy link

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.

@prasetyaputraa
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants