7

I have the following serializer

class MyModelSerializer(serializers.ModelSerializer):
    user = UserSerializer()

    def create(self, validated_data):
        print("TEST")
        MyModel, created = MyModel.objects.get_or_create(**validated_data)
        return MyModel

    class Meta:
        model = MyModel
        fields = ('pk', 'title', 'user', 'movie', 'timestamp', 'text',)

and the following viewset:

class MyModelViewSet(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

When I make an POST request to the endpoint corresponding to specified viewset, the create() method does absolutely nothing. I tried to print out in console TEST as you can see, but nothing.

Does anyone have an idea about this strange behavior?

Thanks in advace!

Edit: API call:

return axios({
  method: 'post',
  url: 'http://localhost:8000/api/mymodel/',
  data: {
     title: this.title,
     movie: this.id,
     text: this.text,
     user: this.user
}
yierstem
  • 1,933
  • 5
  • 21
  • 42

3 Answers3

6

If you look at the implementation of POST handling in a ViewSet, you can find this:

def create(self, request, *args, **kwargs):
    serializer = self.get_serializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    self.perform_create(serializer)
    headers = self.get_success_headers(serializer.data)
    return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

Your create method is called after serializer validates the data.

The error you see (user already exists) is a result of calling serializer.is_valid from the snippet above.

Therefore, it never gets to call your create. Your create would be called as part of self.perform_create() from this snippet above.

So this means that you are trying to create a user which already exists. So in your model you have unique username.

Josh Correia
  • 3,807
  • 3
  • 33
  • 50
Enthusiast Martin
  • 3,041
  • 2
  • 12
  • 20
  • 1
    I've been through source code, I ended up at the same conclusion, but I don't know how can I get around it, is there a way to change serializer data before it gets validated? Should I modify serializer.initial_data beforehand? Thanks – yierstem Sep 27 '18 at 06:05
  • @yierstem why you want to do that ? it is good practice to validate the data. And you must do it anyway. Serializer won't let you call save without calling is_valid first. if you dont want to validate data and just call create, just dont use the serializer at all. – Enthusiast Martin Sep 27 '18 at 06:27
  • I don't want to exclude the data validation, I want to change the data that gets passed to the serializer. for some reason, it tries to create a user record when I just want to get the user data for a specific instance of the serialized model. I want it behave like normal foreign keys in Django, I submit the user pk, the view gets user pk from the request to get the necessary user object (not to create it), to create a mymodel record, so I can retrieve it when I send a get request to mymodel viewset. – yierstem Sep 27 '18 at 07:01
  • It seems that you want to do update then? so instead of POST, do the PUT/PATCH. this will get an instance and update the fields which you want – Enthusiast Martin Sep 27 '18 at 07:04
  • I'm not sure why would it be a put/patch if I want to create new records for mymodel and not change anything about the user, I'm not following. – yierstem Sep 27 '18 at 09:38
  • @yierstem you said this " I just want to get the user data for a specific instance....gets user pk from the request to get the necessary user object (not to create it) " .. .so , it looks like update to me. – Enthusiast Martin Sep 27 '18 at 09:58
  • Yes, but it should be a create view, cause I don't have mymodels entry yet, so I don't have the record created in order to update it. For example, in mymodel serializer I have movie field, which is a foreign key as well as user field, but for some reason, specifying the user field as a serializer won't work, it tries to post data to the user field as well, which I do not want to happen. When I send a post request, I fill the movie field (which is a foreign key, as I mentioned) with the pk of that existing movie. – yierstem Sep 27 '18 at 10:20
2

Ok. I found an alternative. Since I only wanted the username from user object, I removed user = UserSerializer() and I added user_username = serializers.ReadOnlyField(source='user.username'):

class ReviewSerializer(serializers.ModelSerializer):
    user_username = serializers.ReadOnlyField(source='user.username')

    class Meta:
        model = Review
        fields = ('pk', 'title', 'user', 'user_username', 'movie', 'timestamp', 'review_text',)

user = UserSerializer() gave me headaches, so I got around it. I will check Entushiast Martin answer as a solution since they drove me to the actual answer. Thank you.

Solved.

yierstem
  • 1,933
  • 5
  • 21
  • 42
0

It might because you have some sort of error handling in place, because the way your def create function is written will throw an error. I tested the below code and it worked for me give it a go:

class MyModelSerializer(serializers.ModelSerializer):
    user = UserSerializer()

    class Meta:
        model = MyModel
        fields = ('pk', 'title', 'user', 'movie', 'timestamp', 'text',)

    def create(self, validated_data):
        print("TEST")
        data = validated_data
        data, created = MyModel.objects.get_or_create(**data)
        return data

Hope this helps!

Paul Tuckett
  • 1,023
  • 11
  • 14