Можно ли задать классы разрешений для каждой функции в представлениях на основе классов?

Допустим, у нас есть следующее представление на основе классов (CBV), реализующее DRF 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)

При использовании permission_classes для установки политики аутентификации в CBV, политика применяется ко всем методам класса. Возможно ли установить разрешение на функцию в CBV, например

class ExampleListAPIView(APIView):
    def get(self, request): # Apply [AllowAny]
        # logic

    def post(self, request): # Apply [IsAuthenticated]
        # logic

Или вы должны использовать представления на основе функций, чтобы добиться такого поведения?

Вы можете сделать что-то, что проверяет разрешение только для определенного метода:

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

и работать с:

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

Вы даже можете работать с этим как с алгеброй разрешений:

PostAuthenticated = ForMethod(AllowAny, 'get') & ForMethod(
    IsAuthenticated, 'post'
)


class ExampleListAPIView(APIView):
    permission_classes = [PostAuthenticated]
    # …

Основываясь на ответе willeM_ Van Onsem, я размещаю обходной путь, который решает проблему с объединением кастомных разрешений (см. мой комментарий).

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

Это очень похоже на вышеупомянутый ответ, за исключением использования словаря для определения разрешений как:

# {<request.method>: <built-in permission>}
Вернуться на верх