Reputation: 5483
I don't seem to be triggering the has_object_permission
function.
class MyPermission(DefaultPermission):
def has_permission(self, request, view):
return request.user.is_superuser
def has_object_permission(self, request, view, obj):
import pdb;pdb.set_trace()
return request.user == obj.submitter
Going to mydomain.com/api/mymodel/100
, this should be considered accessing the object, correct? I am viewing object 100
here. Why isn't my trace()
being picked up?
class MyModelViewSet(viewsets.ModelViewSet):
serializer_class = MyModelSerializer
permission_classes = (MyPermission,)
def get_queryset(self):
queryset = MyModel.objects.all()
return queryset
Upvotes: 1
Views: 1763
Reputation: 3378
In a DRF generic views check_permissions(request)
is always called in every request since it is inside the dispatch method.
check_permissions
method collects all the permissions from the permission classes and checks each permission. If any permission return false the method raises an exception.
So if your has_permission
method returns false has_object_permission
is not called.
check_object_permissions(request, obj)
calls has_object_permission
method on each permission class. It is called inside get_object
method.
So the rule is if has_permission
for all permission classes returns true then and only then has_object_permission
is checked.
class MyPermission(DefaultPermission):
def has_permission(self, request, view):
# check if the request is for a single object
if view.lookup_url_kwarg in view.kwargs:
return True
return request.user.is_superuser
def has_object_permission(self, request, view, obj):
import pdb;pdb.set_trace()
return request.user == obj.submitter
Upvotes: 4
Reputation: 9235
Permission won't work because check_object_permissions method is called only in get_object function. So you should call that function in your views:
def check(self, request, pk=None):
obj = self.get_object()
Or you could add permissions directly in the decorator,
Example
class MyViewSet(ModelViewSet):
queryset = MyModel.objects.all()
....
permission_classes = (MyPermission, )
@detail_route(methods=['GET', ])
def custom(self, request, pk=None):
my_obj = self.get_object() # do this and your permissions shall be checked
return Response('whatever')
From the docs(django-rest-framework)
Note: The instance-level has_object_permission method will only be called if the view-level has_permission checks have already passed. Also note that in order for the instance-level checks to run, the view code should explicitly call .check_object_permissions(request, obj). If you are using the generic views then this will be handled for you by default.
Upvotes: 1