Django REST Framework viewset per-action permissions

lWhitmore picture lWhitmore · Oct 11, 2013 · Viewed 28k times · Source

Is there a best practice to assign a different permission to each action of a given APIView or ViewSet?

Let's suppose I defined some permissions classes such as 'IsAdmin', 'IsRole1', 'IsRole2', ..., and I want to grant different permissions to the single actions (e.g. a user with Role1 can create or retrieve, a user with Role2 can update, and only an Admin can delete).

How can I structure a class based view in order to assign a permission class to the 'create', 'list', 'retrieve', 'update', 'delete' actions? I'm trying to do so to have a class that can be reused for different tables that have the same permission pattern.

Maybe I'm just drowning in an inch of water, thank you for your replies.

Answer

Chemical Programmer picture Chemical Programmer · Dec 8, 2015

In DRF documentation,

Note: The instance-level has_object_permission method will only be called if the view-level has_permission checks have already passed

Let's assume following permission about user object

  • List : staff only
  • Create : anyone
  • Retrieve : own self or staff
  • Update, Partial update : own self or staff
  • Destroy : staff only

permissons.py

from rest_framework import permissions

class UserPermission(permissions.BasePermission):

    def has_permission(self, request, view):
        if view.action == 'list':
            return request.user.is_authenticated() and request.user.is_admin
        elif view.action == 'create':
            return True
        elif view.action in ['retrieve', 'update', 'partial_update', 'destroy']:
            return True
        else:
            return False

    def has_object_permission(self, request, view, obj):
        # Deny actions on objects if the user is not authenticated
        if not request.user.is_authenticated():
            return False

        if view.action == 'retrieve':
            return obj == request.user or request.user.is_admin
        elif view.action in ['update', 'partial_update']:
            return obj == request.user or request.user.is_admin
        elif view.action == 'destroy':
            return request.user.is_admin
        else:
            return False

views.py

from .models import User
from .permissions import UserPermission
from .serializers import UserSerializer
from rest_framework import viewsets


class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = (UserPermission,)

EDIT

For Django 2.0 replace is_authenticated() with is_authenticated. The method has been turned into an attribute.