Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django REST Framework perform_create: You cannot call `.save()` after accessing `serializer.data`

I currently have the following viewset:

class ArtistProfileViewSet(viewsets.ModelViewSet):
    queryset = ArtistProfile.objects.all()
    serializer_class = ArtistProfileSerializer

    def perform_create(self, serializer):
        if serializer.is_valid():
            serializer.save()

With the following serializers:

class SimpleArtistTrackSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = ArtistTrack
        fields = (...my fields...)


class ArtistProfileSerializer(serializers.HyperlinkedModelSerializer):
    owners = UserSerializer(many=True, required=False)
    tracks = SimpleArtistTrackSerializer(many=True, required=False)

    class Meta:
        model = ArtistProfile
        fields = (...my fields...)

I am getting the following error:

AssertionError: 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. 

I don't see where I would be editing the serializer.data object. Is there something I am missing that would cause this edit? How can I resolve this error?

like image 660
Kerry Ritter Avatar asked Apr 22 '16 01:04

Kerry Ritter


2 Answers

If you are using PyCharm and you were debugging some code, check if you hadn't called serializer.data in your debugger "watches". It call .data everytime your debugger stop in given context(breakpoint) that given serializer appear, even if you don't call it explicit(serializer.data) in your code.

like image 199
Rafał Avatar answered Oct 31 '22 22:10

Rafał


You don't need to call is_valid there at all. When you do an update the serializer is initialized with the model instance (based of the pk in the route, e.g PATCH /artists/{3}/). You would call is_valid() if you are passing data to the serializer as in:

ser = MySerializer(data=request.data) # validate incoming post, etc
ser.is_valid(raise_exceptions=True)
data = ser.validated_data

To send additional data to the serializer when saving (e.g. to set a company, or set a user, etc), use an override like this:

def perform_create(self, serializer):
    serializer.save(company=self.request.user.company)

For more details, I would browse the DRF source code and see what it is doing. it's very well written and clear.

Your comment indicates another question which is much bigger. You say you want to add additional data to the "join" table between artists and user. This can be done, but is a whole different topic and you end up manually managing the relationship. Also, read up on nested writable serializers, it's a big topic and even more manual labor.

like image 31
Andrew Avatar answered Oct 31 '22 23:10

Andrew