6

Why doesn't django rest framework check object permissions when creating an object? It makes no sense (to me, anyway) that a user should be able to create an object they couldn't see, update, or delete. Currently I subclass a viewset like

class CheckCreatePermissionsViewSet(ModelViewSet):
    def perform_create(self, serializer):
    '''
    Called by create before calling serializer.save()
    '''
    obj = serializer.save()
    try:
        self.check_object_permissions(obj)
    except:
        obj.delete()
        raise

Why isn't this implemented by default? It's caused quite the headache for me, and I can't think of a single reason it would be implemented like this.

cderwin
  • 425
  • 4
  • 11

3 Answers3

1

I find it way more natural and explicit that you call the business logic validation before creating the object rather than create the object and figuring whether or not the user can see it to delete it.

class CheckCreatePermissionsViewSet(ModelViewSet):
    def perform_create(self, serializer):
        try:
            business_rules_are_ok(serializer.data, user):
        except BusinessException:
            raise ValidationError(<content from BusinessException)
        serializer.save()
Linovia
  • 19,812
  • 4
  • 47
  • 48
  • If think, you should use `serializer.validated_data` rather than `serializer.data` – luc Jul 11 '17 at 09:33
0

If your intention is not to create an object if user doesn't have certain permission, you can use permission_classes inside Viewset so that viewset won't even allow user to create an object.

Why do you need to created then again delete based on permission?. Its better to check permission before so that object wont even get created if permission fail.

for e.g you want only Admin to create object, you can add permission_classes=[permissions.IsAdminUser] to the viewset, So that control won't even come inside perform_create. Viewset will send 403 if normal user tried to created object. Hope this is what you wanted.

0

In my approach I used the perform_create to do the job like you proposed. However instead of creating and deleting it I used the transaction to take care of it, the code:

class CheckCreatePermissionsViewSet(ModelViewSet):
    def check_object_permissions(self, request, obj):
        super(type(self), self).check_object_permissions(request, obj)
        #Additional logic....
        if not request.user.is_staff:
            raise PermissionDenied()


    def perform_create(self, serializer):
        with transaction.atomic():
            super(type(self), self).perform_create(serializer)
            obj = serializer.instance
            self.check_object_permissions(self.request, obj)