Ben
Ben

Reputation: 4110

django rest framework save serializer fail with post method

I'm trying to update a profile object with post method, but I get a error message when trying to save my serializer :

You cannot call `.save()` after accessing `serializer.data`.If you need to access data before committing to the database then inspect 'serializer.validated_data' instead. 

My view :

class SettingsProfileView(APIView):
    """
    Get and update user profile
    """
    queryset = models.UserProfile.objects.all()
    serializer_class = serializers.UserProfileSerializer

    renderer_classes = [TemplateHTMLRenderer]
    template_name = 'base_/settings/profile.html'

    def get_object(self, pk):
        try:
            return models.UserProfile.objects.get(pk=pk)
        except models.UserProfile.DoesNotExist:
            raise Http404

    def get(self, request, format=None):
        if not request.user.is_authenticated:
            return Response({"error": _("User is not connected")}, status=status.HTTP_511_NETWORK_AUTHENTICATION_REQUIRED)

        try:
            profile = request.user.profile
        except models.UserProfile.DoesNotExist:
            profile = models.UserProfile(user=request.user)
            profile.key_name = request.user.username
            profile.save()

        profile = self.get_object(request.user.profile.id)
        serializer = serializers.UserProfileSerializer(profile)
        return Response({'serializer': serializer, 'profile': profile})


    def post(self, request, format=None):
        serializer = serializers.UserProfileSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 

The error occured in this part : serializer.save() in my post method. Is it because the serializer is accessing data in his instentiation method ? My serializer is very basic, it has no special code :

class UserProfileSerializer(serializers.ModelSerializer):

    class Meta:
        model = UserProfile
        fields = ('user', 'coachs', 'is_coach', 'gender', 'weight', 'height', 'visibility', )

Maybe the problem comes from the fact that I'm using post methode instead of update ?

EDIT (after @pleasedontbelong post) :

I've tried with generic view :

class SettingsProfileView(generics.GenericAPIView): 

but the update method is not fired (because I come from a HTML post), so I had to manually raise update method like that :

 def post(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

 def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object(request.user.profile.id)
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)
        return Response(serializer.data)

But the error is still the same. I cannot find any example where an object is updated with django rest by post method. Is it because it's not a good way to proceed ?

Upvotes: 1

Views: 2307

Answers (2)

Ben
Ben

Reputation: 4110

After hours of debugging, it appear that the problem comes from Visual Studio's breakpoints. After removing breakpoints it works fine. Maybe Visual Studio try to read in serializer.data and then affects them.

Upvotes: 4

pleasedontbelong
pleasedontbelong

Reputation: 20102

It's better to use generic views, it prevents you from rewriting all this code. But if you rather do it manually, you can always check the source code to check how it's done:

https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/mixins.py#L61-L78

class UpdateModelMixin(object):
    """
    Update a model instance.
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)
        return Response(serializer.data)

    def perform_update(self, serializer):
        serializer.save()

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)

you must pass the UserProfile instance to the serializer

BTW: you should/must use PUT or PATCH for updating, POST is for creating objects.

Hope this helps :)

Upvotes: 0

Related Questions