Field level validation with request.user in Django rest framework

Viewed 674

I am setting up a Django REST application where peopple can review restaurants. So far I have those models:

class RestaurantId(models.Model):
    maps_id = models.CharField(max_length=140, unique=True)
    adress = models.CharField(max_length=240)
    name = models.CharField(max_length=140)
        

class RestaurantReview(models.Model):    
    review_author = models.ForeignKey(settings.AUTH_USER_MODEL,
                                      on_delete=models.CASCADE)    
    restaurant_id = models.ForeignKey(RestaurantId, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    

class StarterPics(models.Model):
    restaurant_review_id = models.OneToOneField(RestaurantReview,
                                               on_delete=models.CASCADE)
    pics_author = models.ForeignKey(User, on_delete=models.CASCADE)
    restaurant_id = models.ForeignKey(RestaurantId, on_delete=models.CASCADE)
    name_1 = models.CharField(max_length=40)    
    picture_1 = models.ImageField()

My serializers:

class RestaurantIdSerializer(serializers.ModelSerializer):
    class Meta:
        model = RestaurantId
        field = fields = '__all__'


class RestaurantReviewSerializer(serializers.ModelSerializer):
    class Meta:
        model = RestaurantReview
        field = fields = '__all__'


class StarterPicsSerializer(serializers.ModelSerializer):
    class Meta:
        model = StarterPics
        fields = '__all__'

   def validate_restaurant_review_id(self, value)
        if value.review_author != self.request.user:                
            raise serializers.ValidationError("User has not reviewed the restaurant")        
        return value

My views:

class RestaurantIdViewset(viewsets.ModelViewSet):
    queryset = models.RestaurantId.objects.all()
    serializer_class = serializers.RestaurantIdSerializer

class RestaurantReviewViewset(viewsets.ModelViewSet):
    queryset = models.RestaurantReview.objects.all()
    serializer_class = serializers.RestaurantReviewSerializer
    permission_classes = [IsAuthenticatedOrReadOnly,IsAuthorOrReadOnly]

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

class StarterPicsViewset(viewsets.ModelViewSet):
    queryset = models.StarterPics.objects.all()
    serializer_class = serializers.StarterPicsSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]
I have set up permissions as well so only the review_author can update his reviews and pics_author can update his pictures.

My permissions:

class IsOwnReviewOrReadOnly(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True

        return obj.pics_author == request.user


class IsAuthorOrReadOnly(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True

    return obj.review_author == request.user

When running Django server I got a 'StarterPicsSerializer' object has no attribute 'request'

This validation is for user that have not written the review (review_author) can't POST pictures in StarterPics. So only the User that creates the review can post pictures on it.

I've tried another validation with no luck either:

def validate_restaurant_review_id(self, value):               
    if not RestaurantReview.objects.filter(restaurant_review_id=value, 
                     review_author=self.request.user).exists():              
        raise serializers.ValidationError('Not your review')  
    return value
1 Answers

You could provide extra context to the serializer in addition to the object being serialized by passing a context argument when instantiating the serializer in your view.

serializer = RandomSerializer(instance, context={'request': request})


If you use Generic Views or ModelViewSet(inherited form GenericAPIView), then request is already available in your serializer self.context dict

class StarterPicsSerializer(serializers.ModelSerializer):
    class Meta:
        model = StarterPics
        fields = '__all__'

    def validate_restaurant_review_id(self, value):                     
        print(self.context['request'])
Related