6

I would like to manage my objects permission using django-guardian in a restful project (using django-rest-framework).

What I would like :

  • Allow the connected user to create an object only if he have the "add_modelname" permission.
  • When the connected user create an object, set the "delete_modelname" and "change_modelname" permission.
  • Allow the connected user to edit an object only if he have the "change_modelobject" permission.
  • Allow the connected user to delete an object only if he have the "delete_modelobject" permission.

I'm trying to manage thoses cases with this code :

view.py

class ModelNameViewSet(viewsets.ModelViewSet):
    """
    This viewset automatically provides `list`, `create`, `retrieve`,
    `update` and `destroy` actions.

    Additionally we also provide an extra `highlight` action.
    """
    queryset = ModelName.objects.all()
    serializer_class = ModelNameSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly, ModelNamePermission)

    def create(self, request, *args, **kwargs):
        assign_perm("change_modelname", request.user, self)
        assign_perm("delete_modelname", request.user, self)
        return super().create(request, *args, **kwargs)

permissions.py

class ModelNamePermission(permissions.BasePermission):
    """
    Custom permission to only allow owners of an object to edit it.
    """

    def has_permission(self, request, view):
        if request.method in ['GET']:
            return request.user.has_perm('view_modelname')        
        if request.method in ['POST']:
            return request.user.has_perm('add_modelname')
        if request.method in ['PUT', 'PATCH']:
            return request.user.has_perm('change_modelname')
        if request.method in ['DELETE']:
            return request.user.has_perm('delete_modelname')
        return False

    def has_object_permission(self, request, view, obj):         
        if request.method in ['GET']:
            return request.user.has_perm('view_modelname', obj)        
        if request.method in ['POST']:
            return request.user.has_perm('add_modelname', obj)
        if request.method in ['PUT', 'PATCH']:
            return request.user.has_perm('change_modelname', obj)
        if request.method in ['DELETE']:
            return request.user.has_perm('delete_modelname', obj)
        return False

The first problem I encounter is that I have an error in this line :

assign_perm("change_modelname", request.user, self)

error :

error: 'ModelNameViewSet' object has no attribute '_meta'

And I think that the rest of the code will not works but at least you can see what I want to do.

I haven't seen any example with thoses specifics cases.

Edit : Another thing is that this code :

request.user.has_perm('view_coachingrequest')

allways returns true. But I've never set this permission to my user (only tried with admin user, maybe that's why).

demongolem
  • 9,474
  • 36
  • 90
  • 105
Ben
  • 3,972
  • 8
  • 43
  • 82

1 Answers1

2

The create method of a viewset will return you a freshly created model instance. You are trying to assign the permission to the viewset object itself. The code for your create method should be as follows:

def create(self, request, *args, **kwargs):
    instance = super().create(request, *args, **kwargs)
    assign_perm("change_modelname", request.user, instance)
    assign_perm("delete_modelname", request.user, instance)
    return instance

This will create the model instance and then assign your desired permissions to it before returning it.

Conrad Calmez
  • 96
  • 1
  • 5
  • 1
    The create method in a DRF viewset returns a response object, not an instance of the model. Code here: https://github.com/encode/django-rest-framework/blob/a81e60ff390b9b6788b2cac24a01ee4dd2dcffd2/rest_framework/mixins.py#L14 Are you thinking about the create method in a serializer? – thclark May 25 '18 at 19:26
  • Actually it can be done either in the serializer or in the perform_create() method on the viewset (the latter shown by answer here: https://stackoverflow.com/questions/41230236/set-object-level-permission-with-django-rest-framework ). So the choice is yours as to where to keep that code. For me, I think I'd do it in the serializer (which might allow you to write cleaner error handling code, having the object creation in the same place as permissions assignment) – thclark May 27 '18 at 10:41