2
votes

I am using Django Rest Framework for my API service. In my settings.py I've got following REST_FRAMEWORK setting:

REST_FRAMEWORK = {
    ...
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated'
    )
    ...
}

Now I want to change permission classes to allow anyone to use one view. I had used @permission_classes decorator.

class UserViewSet(viewsets.ModelViewSet):

    serializer_class = RegisterSerializer
    queryset = User.objects.all()

    @permission_classes([permissions.AllowAny,])
    def create(self, request, *args, **kwargs):
        data = request.data
        ...

I should be able to perform create action without any permissions. Instead I receive Authentication error when try to access the API.

    "detail": "Authentication credentials were not provided."

According to docs @permission_classes decorator is correct way for such permission overriding. This is Django 2.2.4 and DRF 3.10.2.

EDIT

I forgot to mention that I do not want to allow any one to access any view, only the chosen one. Most of views should have IsAuthenticated permission which is set globally.

5

5 Answers

4
votes

The default permission policy may be set globally, using the DEFAULT_PERMISSION_CLASSES setting. For example.

REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
    'rest_framework.permissions.IsAuthenticated',
]}

If not specified, this setting defaults to allowing unrestricted access:

REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
    'rest_framework.permissions.AllowAny',
]}

for further reference you can check DRF document doc

EDIT:

You can override the permission, with your own class as:

from rest_framework import permissions

class SkipAuth(permissions.IsAuthenticated):
    def has_permission(self, request, view):
    return True

And use this class to skip the authentication of particular function as:

@permission_classes([SkipAuth])
def create(self, request, *args, **kwargs):
   data = request.data
   ...
3
votes

You can just override the get_permission method

class UserViewSet(viewsets.ModelViewSet):

  serializer_class = RegisterSerializer
  queryset = User.objects.all()

  def get_permissions(self):
    if self.request.method == 'POST':  # remove default permission from post method(Create method)
       return []
    return [permission() for permission in self.permission_classes]

  def create(self, request, *args, **kwargs):
    data = request.data
    ...
2
votes

You have specified permission class to IsAuthenticated but you want to allow it to any users so you have to remove that permission from settings and by default django will allow to any or you can change this to

'DEFAULT_PERMISSION_CLASSES': [
   'rest_framework.permissions.AllowAny',
]

See the docs for more information

2
votes

In the viewset, you should use permission_classes class attribute instead of method decorator.

So your UserViewSet would look like:

class UserViewSet(viewsets.ModelViewSet):

    serializer_class = RegisterSerializer
    queryset = User.objects.all()

    permission_classes = (permissions.AllowAny,)

    def create(self, request, *args, **kwargs):
        data = request.data
        ...

In this case, the permission should work as you'd expect.

2
votes

You can overwrite check_permission method of your desire view function and achieve that.

class UserViewSet(viewsets.ModelViewSet):

    serializer_class = RegisterSerializer
    queryset = User.objects.all()

    def check_permissions(self, request):
       if self.action and (self.action == 'create'):
          return True // allow any
       return super().check_permissions(request)

    def create(self, request, *args, **kwargs):
        data = request.data
        ...