Alvaro Bataller
Alvaro Bataller

Reputation: 483

Django Rest Framework - "unique_together" internal server error. How to respond with error

I currently have a model called "Review" where I have a "unique_together" constraint so one user can only write one review per post.

class Review(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    user = models.ForeignKey(User, related_name='reviews', on_delete=models.SET_NULL, null=True)
    post = models.ForeignKey(Post, related_name='reviews', on_delete=models.CASCADE)
    rating = models.IntegerField()
    comment = models.TextField(blank=True, null=True)

    class Meta:
        unique_together = ['user', 'post']

And in my View I am grabbing the user from the request (I'm using token authentication) and use that user to save the object by overwriting the "perform_create" method:

class ReviewCreateView(CreateAPIView):
    permission_classes = [IsAuthenticated]
    serializer_class = ReviewCreateSerializer

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

And my serializer looks like this:

class ReviewCreateSerializer(ModelSerializer):
    class Meta:
        model = Review
        fields = ['post', 'rating', 'comment']

When I try to submit two reviews to the same post by the same user I get an internal server error.

The body of my HTTP POST request looks like this:

{
    "post" : 11,
    "rating": 1,
    "comment": "this is a great post"
}

Instead of getting an error response (for example 403 error). I get this Django server error instead:

django.db.utils.IntegrityError: duplicate key value violates unique constraint

After spending some time researching I saw that Django Rest Framework provides the "UniqueTogetherValidator". So I added it like this:

class ReviewCreateSerializer(ModelSerializer):
    class Meta:
        model = Review
        fields = ['post', 'rating', 'comment']
        validators = [
                UniqueTogetherValidator(
                    queryset=Review.objects.all(),
                    fields=['user', 'post']
                )
            ]

But then the issue I'm having is that it errors because it can't find the key "user", I'm guessing because I dont have a "user" field in my HTTP request body (since the user is being grabbed by Django using the authentication token). I get the following error:

KeyError: 'user'

I also tried adding the 'user' to my serializer fields:

fields = ['user', 'post', 'rating', 'comment']

But I get the following error response:

{
    "user": [
        "This field is required."
    ]
}

I would really appreciate some help to solve this issue. Many thanks!

Upvotes: 1

Views: 1155

Answers (1)

Ken4scholars
Ken4scholars

Reputation: 6296

Try using the CurrentUserDefault serializer field so that the user can be retrieved implicitly for the validation.

class ReviewCreateSerializer(ModelSerializer):
    user = serializers.HiddenField(
        default=serializers.CurrentUserDefault()
    )
    class Meta:
        model = Review
        fields = fields = ['user', 'post', 'rating', 'comment']
        validators = [
                UniqueTogetherValidator(
                    queryset=Review.objects.all(),
                    fields=['user', 'post']
                )
            ]

Also, with this, you no longer have to pass the user to the serializer save method as it is already present in the serializer. So you can get rid of the overridden perform_create() method in your view

Upvotes: 2

Related Questions