How to apply permissions on perform_create in ViewSet DRF

This is my View Set:

class MyViewSet(ModelViewSet):
    serializer_class = MySerializer
    queryset = MyClass.objects.all()

   def get_serializer_class(self):
        if self.request.user.is_superuser:
            return self.serializer_class 

        return serializers.MyUserSerializer   

    def perform_create(self, serializer):
        employee = models.Employee.objects.get(user=self.request.user)
        serializer.save(employee=employee)

I want to apply permission before perform_create, this perform_create() should only be called if a currently logged in user is not a super user. If a currently logged in user is a superuser, default perform_create function should be called.

How to do that?

Try this logic

def perform_create(self, serializer):
  self.request.data.get("title", None)  # read data from request
  if self.request.user.is_authenticated and not self.request.user.is_superuser:
      instance = serializer.save(author=self.request.user)
  else:
      instance = serializer.save() 

You can use permission_classes in your Viewset class

from rest_framework.permissions import IsAuthenticated
class MyViewSet(ModelViewSet):
    permission_classes = (IsAuthenticated,)
    serializer_class = MySerializer
    queryset = MyClass.objects.all()

    ...

Try like this:

class MyViewSet(ModelViewSet):
    serializer_class = MySerializer
    queryset = MyClass.objects.all()

    def get_serializer_class(self):
        if self.request.user.is_superuser:
            return self.serializer_class 

        return serializers.MyUserSerializer   

    def perform_create(self, serializer):
        employee = models.Employee.objects.get(user=self.request.user)
        serializer.save(employee=employee)

    def has_permission(self, request, view):
        if request.user.is_superuser:
            return True
        return super().has_permission(request, view)

perform_create method will be called only then if the user is not a superuser and have permissions. has_permission method will returns True for superusers.

You can write a custom permission class like this:

class CustomPermission(permissions.BasePermission):

    def has_permission(self, request, view):
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True
        # write permission is allowed only for superusers
        return request.user.is_superuser

Then use this permission in your view:

class MyViewSet(ModelViewSet):
    permission_classes = (IsAuthenticated, CustomPermission,)
    serializer_class = MySerializer

Unfortunately, the logic you want to implement using Permission is not possible because perform_create does execute object level permission, hence the logic needs to be in perform_create method like you mentioned in the question.

You can create a Custom permission see the following example.

from rest_framework import permissions

class IsNotSuperuserPermission(permissions.BasePermission):
    message = 'You are super user.' # Your custom message.

    def has_permission(self, request, view):
        if request.user.is_authenticated:
            return not request.user.is_superuser
        self.message = 'you are not logged in' # your custom message
        return False



class MyViewSet(ModelViewSet):
    serializer_class = MySerializer
    queryset = MyClass.objects.all()

    def get_serializer_class(self):
        if self.request.user.is_superuser:
            return self.serializer_class 

        return serializers.MyUserSerializer 

    def get_permissions(self):
        if self.action == 'create':
            permission_classes = [IsNotSuperuserPermission]
        else:
            permission_classes = [IsAuthenticated]
        return [permission() for permission in permission_classes]

    def perform_create(self, serializer):
        employee = models.Employee.objects.get(user=self.request.user)
        serializer.save(employee=employee)

in get_permissions you can assign one or more permission to every action.

Back to Top