Можно ли задать классы разрешений для каждой функции в представлениях на основе классов?
Допустим, у нас есть следующее представление на основе классов (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>}