Del Hyman-Jones
2018-07-30 21:14:43 UTC
The example here (
http://www.django-rest-framework.org/api-guide/viewsets/#marking-extra-actions-for-routing)
of the documentation suggests that *permission_classes* can be specified as
an attribute to the *@action* decorator but there does not seem to be any
code in the rest-framework to support this. Here is a snippet from the docs:
@action(methods=['post'], detail=True, permission_classes=[
IsAdminOrIsSelf])
def set_password(self, request, pk=None):
...
Example Code
The example code I used is here:
views.py
from rest_framework import permissions
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
class MyPermission(permissions.BasePermission):
def has_permission(self, request, view):
return True
class MyViewSet(viewsets.GenericViewSet):
permission_classes = (permissions.IsAuthenticated,)
@action(detail=True, permission_classes=[MyPermission])
def test(self, request, *args, **kwargs):
return Response(status.HTTP_204_NO_CONTENT)
@action(detail=True)
def test2(self, request, *args, **kwargs):
return Response(status.HTTP_204_NO_CONTENT)
urls.py
urlpatterns = [
path('admin/', admin.site.urls),
url(r'^api-auth/', include('rest_framework.urls')),
url(r'^api/test/$', MyViewSet.as_view(actions={'get': 'test'})),
url(r'^api/test2/$', MyViewSet.as_view(actions={'get': 'test2'})),
]
When tracing the framework code only *IsAuthenticated* is referred to
because it is set in the *MyViewSet* class instance *permission_classes*
variable.* MyPermission.has_permission* is never called.
The action decorator code simply adds *permission_classes* as a variable to
the function object and not to *MyViewSet().permission_classes* as is
required by the framework's *get-permissions* function. (
https://github.com/encode/django-rest-framework/blob/8493990a66e36d5dd4a62742d861b5b6b15cae80/rest_framework/views.py#L276-L280
)
Framework code change suggestion
If the built-in *rest_framework.views.APIView.get_permissions* function was
changed to the following it would automatically support the *permissions_classes
*attribute of *@action* and if that was not specified then default's to
* MyViewSet().permission_classes *in my example.
def get_permissions(self):
func_actions = {
action_details.url_name: action_details.kwargs.get(
'permission_classes'
) for action_details in self.get_extra_actions()
}
perm_classes = func_actions.get(self.action) or self.
permission_classes
return [permission() for permission in perm_classes]
Can anyone tell me if either the documentation is wrong, the framework code
does not match the documentation or I've misunderstood something here?
Assuming I haven't misunderstood, does anyone think that the code change
suggestion is worth mentioning to the DRF developers?
Cheers
Del
http://www.django-rest-framework.org/api-guide/viewsets/#marking-extra-actions-for-routing)
of the documentation suggests that *permission_classes* can be specified as
an attribute to the *@action* decorator but there does not seem to be any
code in the rest-framework to support this. Here is a snippet from the docs:
@action(methods=['post'], detail=True, permission_classes=[
IsAdminOrIsSelf])
def set_password(self, request, pk=None):
...
Example Code
The example code I used is here:
views.py
from rest_framework import permissions
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
class MyPermission(permissions.BasePermission):
def has_permission(self, request, view):
return True
class MyViewSet(viewsets.GenericViewSet):
permission_classes = (permissions.IsAuthenticated,)
@action(detail=True, permission_classes=[MyPermission])
def test(self, request, *args, **kwargs):
return Response(status.HTTP_204_NO_CONTENT)
@action(detail=True)
def test2(self, request, *args, **kwargs):
return Response(status.HTTP_204_NO_CONTENT)
urls.py
urlpatterns = [
path('admin/', admin.site.urls),
url(r'^api-auth/', include('rest_framework.urls')),
url(r'^api/test/$', MyViewSet.as_view(actions={'get': 'test'})),
url(r'^api/test2/$', MyViewSet.as_view(actions={'get': 'test2'})),
]
When tracing the framework code only *IsAuthenticated* is referred to
because it is set in the *MyViewSet* class instance *permission_classes*
variable.* MyPermission.has_permission* is never called.
The action decorator code simply adds *permission_classes* as a variable to
the function object and not to *MyViewSet().permission_classes* as is
required by the framework's *get-permissions* function. (
https://github.com/encode/django-rest-framework/blob/8493990a66e36d5dd4a62742d861b5b6b15cae80/rest_framework/views.py#L276-L280
)
Framework code change suggestion
If the built-in *rest_framework.views.APIView.get_permissions* function was
changed to the following it would automatically support the *permissions_classes
*attribute of *@action* and if that was not specified then default's to
* MyViewSet().permission_classes *in my example.
def get_permissions(self):
func_actions = {
action_details.url_name: action_details.kwargs.get(
'permission_classes'
) for action_details in self.get_extra_actions()
}
perm_classes = func_actions.get(self.action) or self.
permission_classes
return [permission() for permission in perm_classes]
Can anyone tell me if either the documentation is wrong, the framework code
does not match the documentation or I've misunderstood something here?
Assuming I haven't misunderstood, does anyone think that the code change
suggestion is worth mentioning to the DRF developers?
Cheers
Del
--
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
You received this message because you are subscribed to the Google Groups "Django REST framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-rest-framework+***@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.