class MyViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MySerializer
authentication_classes = (MyAuth, )
permission_classes = (MyPermissions, )
@detail_route(methods=["GET", ])
def custom(self, request, pk=None):
return Response('whatever')
from rest_framework import routers
router = routers.SimpleRouter()
router.register('mymodel', MyViewSet)
urlpatterns = [
url(r'^api/', include(router.urls)),
]
make a GET request to /api/mymodel/123/custom/
has_object_permission() should be called with MyModel.objects.get(pk=123) as object.
request to the custom route in the form of /api/mymodel/123/custom/ will call has_permission() in MyPermissions, but not has_object_permission() even though it is a detail route.
We need to document the fact that you'll only get the has_object_permission called only if you call the get_object.
This seems counter-intuitive. Calls like /api/object/123/method/ are generally used to call some method on the object instance with ID 123 and it seems that checking object permission would be an expected behavior. Otherwise every time someone makes custom detail routes, they would have to retrieve the object, manually check permissions and proceed. At that point, calling has_object_permission() makes no sense anyway. So in every scenario where object level permissions are used some boilerplate like this would have to be used
class CustomPermission(BasePermission):
def has_permission(self, request, view):
"""we need to do all permission checking here, since has_object_permission() is not guaranteed to be called"""
if 'pk' in view.kwargs and view.kwargs['pk']:
obj = view.get_queryset()[0]
# check object permissions here
else:
# check model permissions here
def has_object_permission(self, request, view, obj):
""" nothing to do here, we already checked everything, so ignore """
return True
I think I get it now. If I call get_object() in my custom routes, has_object_permission() will get called. Still seems counter-intuitive, but at least there is a clear solution.
I'd be happy to consider a concrete pull-request for documentation, but as it stands I think what we've got is about adequate in http://www.django-rest-framework.org/api-guide/permissions/#object-level-permissions
Closing this, but as noted specfic rephrasing/additions _could_ be considered.
Most helpful comment
We need to document the fact that you'll only get the
has_object_permissioncalled only if you call theget_object.