Can you set permission classes per function in class-based views?
Let's say we have the following class-based view (CBV) implementing DRF's APIView
:
class ExampleListAPIView(APIView):
permission_classes = [IsAuthenticatedOrReadOnly]
def get(self, request):
'''
List all examples
'''
examples = Example.objects.all()
serializer = ExampleListSerializer(examples, many=True)
return Response(serializer.data, status.HTTP_200_OK)
def post(self, request):
'''
Create new example
'''
serializer = ExampleListSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Using permission_classes
to set authentication policy in CBV, the policy is applied to all methods of class. Is it possible to set permission per function in a CBV, e.g.
class ExampleListAPIView(APIView):
def get(self, request): # Apply [AllowAny]
# logic
def post(self, request): # Apply [IsAuthenticated]
# logic
Or you have to use function-based views to achieve this behavior?
You could make something that only checks a permission for a certain method:
class ForMethod(BasePermission):
def __init__(self, permission, *methods):
self.permission = permission
self.methods = set(map(str.casefold, methods))
def __call__(self):
return self
def has_permission(self, request, view):
if request.method.casefold() in self.methods:
self.permission().has_permission(request, view)
return True
def has_object_permission(self, request, view, obj):
if request.method.casefold() in self.methods:
self.permission().has_object_permission(request, view, obj)
return True
and work with:
class ExampleListAPIView(APIView):
permission_classes = [
ForMethod(AllowAny, 'get'),
ForMethod(IsAuthenticated, 'post'),
]
def get(self, request): # Apply [AllowAny]
# logic
pass
def post(self, request): # Apply [IsAuthenticated]
# logic
pass
You can even work with this as an algebra of permissions:
PostAuthenticated = ForMethod(AllowAny, 'get') & ForMethod(
IsAuthenticated, 'post'
)
class ExampleListAPIView(APIView):
permission_classes = [PostAuthenticated]
# …
Based on willeM_ Van Onsem's answer, I'm posting a workaround that overcomes the issue with combining custom permissions (see my comment).
class MyPermission(BasePermission):
def __init__(self, permissions_dict):
self.permissions_dict = {k.casefold():v for k, v in permissions_dict.items()}
def __call__(self):
return self
def has_permission(self, request, view):
if request.method.casefold() in self.permissions_dict.keys():
return self.permissions_dict[request.method.casefold()]().has_permission(request, view)
return False
def has_object_permission(self, request, view, obj):
if request.method.casefold() in self.permissions_dict.keys():
return self.permissions_dict[request.method.casefold()]().has_object_permission(request, view, obj)
return False
class ExampleAPIView(APIView):
permissions_dict = {'GET': AllowAny,
'POST': IsAuthenticated}
permission_classes = [
MyPermission(permissions_dict)
]
# code
It's very similar to aforementioned answer, except for using a dictionary to define permissions as:
# {<request.method>: <built-in permission>}