DRF IntegrityError: NOT NULL constraint failed: user_id
Asked Answered
A

2

1

I can't figure out how to pass user object to the following serializer:

class ReviewSerializer(serializers.ModelSerializer):
    user = UserSerializer(read_only=True)

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

I have this viewset:

class ReviewsViewSet(viewsets.ModelViewSet):
    queryset = Review.objects.all()
    serializer_class = ReviewSerializer

and this model:

class Review(models.Model):
    title = models.CharField(max_length=255)
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reviews')
    movie = models.ForeignKey(Movie, on_delete=models.CASCADE, related_name='reviews')
    review_text = models.TextField()
    timestamp = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return '{movie} review by {user}'.format(user=self.user, movie=self.movie)

My javascript request looks like this:

return axios({
  method: 'post',
  url: 'http://localhost:8000/api/reviews/',
  data: { // Using data from Vue
    title: this.review_title,
    movie: this.id,
    review_text: this.review_text,
    user: JSON.stringify(this.user)
  },
  headers: {
    'Content-Type': 'application/json',
    Authorization: `JWT ${token}`
  }
})

It gives me this traceback.

How should I pass the user object to the request?

Thanks in advance.

Animated answered 26/9, 2018 at 12:56 Comment(0)
C
2

Remove read_only=True from serializer

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

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

If you set read_only=True, the DRF will not takes the value from input source even if it's there

From the doc,

Read-only fields are included in the API output, but should not be included in the input during create or update operations. Any 'read_only' fields that are incorrectly included in the serializer input will be ignored.

Set this to True to ensure that the field is used when serializing a representation, but is not used when creating or updating an instance during deserialization.

Defaults to False


UPDATE
You should override the create() method of ReviewSerializer as

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

    def create(self, validated_data):
        user_dict = validated_data.pop('user')
        user_obj, created = User.objects.get_or_create(**user_dict)
        return Review.objects.create(user=user_obj, **validated_data)

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



for debug purpose only

class ReviewsViewSet(viewsets.ModelViewSet):
    queryset = Review.objects.all()
    serializer_class = ReviewSerializer

    def create(self, request, *args, **kwargs):
        print(request.data)  # print here <<<<
        return super(ReviewsViewSet, self).create(request, *args, **kwargs)
Copilot answered 26/9, 2018 at 12:59 Comment(14)
Yes, but If I take out read_only=True it gives me: "A user with that username already exists.". So, I don't want to post a new user, just to take the user data needed to create that particular review.Animated
Can you add the value of user that you're passing from front-end?Copilot
@Animated Anyway I've updated the answer. Hope it helpsCopilot
I pass the whole user object that I stored in Vuex so I can display user details and stuff. So it looks like this "{"pk":1,"username":"test","first_name":"test","last_name":"test","email":"[email protected]"}". I sent a request with all of the fields required, especially user field, and returns another error: "user": [ "This field is required." ].Animated
Yes, I edited the serializer according to your answer.Animated
The error says that the user data not reaching to DRF viewCopilot
@Animated print the data in DRF view and add it here, (check the updated answe)Copilot
It does not reach create() method. I tried your updated answer, it displays nothing, only Bad request and the status code. Note: I tried to display some random string, to see if it displays anything at all.Animated
create method of view or serializer?Copilot
Oh, sorry, It was for the viewset, since it has request, my bad. Here: <QueryDict: {'review_text': ['something'], 'title': ['Something'], 'user': ['{"pk":1,"username":"test","first_name":"test","last_name":"test","email":"[email protected]"}'], 'movie': ['8']}>Animated
Can you add the result of print(type(request.data['user'])) in create() method of view ?Copilot
<class 'str'> is the outputAnimated
Then, Change user: JSON.stringify(this.user) to user: this.user in your JS codeCopilot
Oh, ok, now it's working. Thank you for detalied answer, I learned a bit how to debug DRF now.Animated
F
0

just add this

class ReviewSerializer(serializers.ModelSerializer):
    user = UserSerializer(read_only=True)
    # new line
    user_id = serializers.IntegerField(write_only=True)

    class Meta:
        model = Review
        fields = (
            'pk', 'title', 'user', 'movie', 'timestamp', 'review_text',
            'user_id', # new field
        )

read_only

Read-only fields are included in the API output, but should not be included in the input during create or update operations. Any 'read_only' fields that are incorrectly included in the serializer input will be ignored.

Set this to True to ensure that the field is used when serializing a representation, but is not used when creating or updating an instance during deserialization.

Defaults to False

write_only

Set this to True to ensure that the field may be used when updating or creating an instance, but is not included when serializing the representation.

Defaults to False

Flocculant answered 21/2 at 13:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.