Neil
Neil

Reputation: 7202

Django REST Framework: Validate before a delete

I want to run a validation before an object is deleted, to prevent deletion in certain cases and return as a validation error. How do I do that? What I have currently doesn't seem right:

class CallDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = XCall.objects.all()
    serializer_class = CallSerializer
    ...

    def pre_delete(self, obj):
        if obj.survey:
            raise serializers.ValidationError("Too late to delete")

Upvotes: 20

Views: 9279

Answers (5)

Oli Crt
Oli Crt

Reputation: 1173

For anyone landing here since V3 like me... :)
You may now use perform_destroy:

def perform_destroy(self, instance):
    if not can_delete(instance):
        raise ValidationError("You are not allowed to perform this action.")
    instance.delete()

Upvotes: 7

Pablo Abdelhay
Pablo Abdelhay

Reputation: 1008

The solution I found was to override the destroy method on the api.

class CallDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = XCall.objects.all()
    serializer_class = CallSerializer
    ...

    def destroy(self, request, *args, **kwargs):
        obj = self.get_object()
        if obj.survey:
            return Response(data={'message': "Too late to delete"},
                            status=status.HTTP_400_BAD_REQUEST)
        self.perform_destroy(obj)
        return Response(status=status.HTTP_204_NO_CONTENT)

For me makes much more sense to validate on the destroy method instead of validating on the object permission check as avances123 mentioned, since permission should only check for permission stuffs, and doesn't return any messages related to validation.

Hope that helps ;)

Upvotes: 9

Neil
Neil

Reputation: 7202

Rather than raising a ValidationError, I will just raise a ParseError or another custom error that fits the error description:

from rest_framework import exceptions
def pre_delete(self, obj):
        if obj.survey:
            raise exceptions.ParseError("Too late to delete")

Upvotes: 2

avances123
avances123

Reputation: 2354

You can solve it with permissions:

from rest_framework import permissions

class IsSurvey(permissions.BasePermission):


    def has_object_permission(self, request, view, obj):

        if request.method == 'DELETE' and obj.survey:
            return False


        return True

Then add to your view's permission_classes

from permissions import IsSurvey

class MyViewSet(viewsets.ModelViewSet):
    permission_classes = (IsSurvey, )

Upvotes: 7

Carlton Gibson
Carlton Gibson

Reputation: 7386

Update

validate doesn't get called on delete.

Hmmm. Right yes. In that case I'd raise the exception in pre_delete just as you are and override delete to wrap the destroy call in a try block.

If you except ValidationError as e you can use that to construct the response you want by hand...

return Response({"error" : e.message})

... or such.

You could probably populate self._errors and leverage the existing error response behaviour but I can't think how to do that off-hand.

I hope that helps.

First Answer (didn't work):

Check the doc on Object Level Validation.

Implement validate to check if the survey is set:

def validate(self, attrs):
    if attrs['survey']
        raise serializers.ValidationError("Too late to delete")
    return attrs

If attrs isn't what you need here you'll have to do something cleverer — but it will get your error in at the validation stage.

I hope that helps.

Upvotes: 0

Related Questions