Django REST Framework - отдельные разрешения для каждого метода
Я пишу API с использованием Django REST Framework, и мне интересно, можно ли указать разрешения для метода при использовании представлений на основе классов.
Чтение документации Я вижу, что это довольно легко сделать, если вы пишете представления на основе функций, просто используя декоратор @permission_classes
над функцией представлений, которые вы хотите защитить с помощью разрешений. Однако я не вижу способа сделать то же самое при использовании CBV с классом APIView
, потому что тогда я указываю разрешения для полного класса с атрибутом permission_classes
, но тогда он будет применен ко всем методам класса. (get
, post
, put
...).
Итак, возможно ли иметь представления API, написанные с использованием CBV, а также указывать различные разрешения для каждого метода класса представления?
Ответы
Ответ 1
Разрешения применяются ко всему классу View, но вы можете учитывать аспекты запроса (например, метод GET или POST) в своем решении о авторизации.
В качестве примера см. встроенный IsAuthenticatedOrReadOnly
:
SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS']
class IsAuthenticatedOrReadOnly(BasePermission):
"""
The request is authenticated as a user, or is a read-only request.
"""
def has_permission(self, request, view):
if (request.method in SAFE_METHODS or
request.user and
request.user.is_authenticated()):
return True
return False
Ответ 2
Я столкнулся с той же проблемой при использовании CBV, поскольку у меня довольно сложная логика разрешений в зависимости от метода запроса.
Решение, с которым я столкнулся, состояло в том, чтобы использовать стороннее приложение "rest_condition", указанное в нижней части этой страницы.
http://www.django-rest-framework.org/api-guide/permissions
https://github.com/caxap/rest_condition
Я просто разделил логику потока разрешений, чтобы каждая ветвь выполнялась в зависимости от метода запроса.
from rest_condition import And, Or, Not
class MyClassBasedView(APIView):
permission_classes = [Or(And(IsReadOnlyRequest, IsAllowedRetrieveThis, IsAllowedRetrieveThat),
And(IsPostRequest, IsAllowedToCreateThis, ...),
And(IsPutPatchRequest, ...),
And(IsDeleteRequest, ...)]
Таким образом, "Or" определяет, какая ветка разрешений должна выполняться в зависимости от метода запроса, а "И" обертывает разрешения, относящиеся к принятому методу запроса, поэтому все должны пройти разрешение на предоставление. Вы также можете смешивать "Or", "And" и "Not" в каждом потоке, чтобы создать еще более сложные разрешения.
Классы разрешений для запуска каждой ветки просто выглядят так:
class IsReadyOnlyRequest(permissions.BasePermission):
def has_permission(self, request, view):
return request.method in permissions.SAFE_METHODS
class IsPostRequest(permissions.BasePermission):
def has_permission(self, request, view):
return request.method == "POST"
... #You get the idea
Ответ 3
Я столкнулся с этой проблемой и действительно хотел использовать декоратор @permission_classes
чтобы пометить некоторые пользовательские методы представления с определенными разрешениями. Я закончил тем, что придумал миксин:
class PermissionsPerMethodMixin(object):
def get_permissions(self):
"""
Allows overriding default permissions with @permission_classes
"""
view = getattr(self, self.action)
if hasattr(view, 'permission_classes'):
return [permission_class() for permission_class in view.permission_classes]
return super().get_permissions()
Пример использования:
from rest_framework.decorators import action, permission_classes # other imports elided
class MyViewset(PermissionsPerMethodMixin, viewsets.ModelViewSet):
permission_classes = (IsAuthenticatedOrReadOnly,) # used for default ViewSet endpoints
queryset = MyModel.objects.all()
serializer_class = MySerializer
@action(detail=False, methods=['get'])
@permission_classes((IsAuthenticated,)) # overrides IsAuthenticatedOrReadOnly
def search(self, request):
return do_search(request) # ...
Ответ 4
Я знаю, что это старый вопрос, но недавно я столкнулся с той же проблемой и хотел поделиться своим решением (поскольку принятый ответ был не совсем тем, что мне было нужно). Ответ @GDorn поставил меня на правильный путь, но он работает только с ViewSet
из-за self.action
Я решил это, создав собственный декоратор:
def method_permission_classes(classes):
def decorator(func):
def decorated_func(self, *args, **kwargs):
self.permission_classes = classes
return func(self, *args, **kwargs)
return decorated_func
return decorator
Вместо установки свойства permission_classes
для функции, как это делает встроенный декоратор, мой декоратор оборачивает вызов и устанавливает классы разрешений для вызываемого экземпляра представления. Таким образом, обычный get_permissions()
не нуждается в каких-либо изменениях, поскольку он просто полагается на self.permission_classes
.
Пример использования:
from rest_framework import views, permissions
class MyView(views.APIView):
permission_classes = (permissions.IsAuthenticatedOrReadOnly,) # used for default APIView endpoints
queryset = MyModel.objects.all()
serializer_class = MySerializer
@method_permission_classes((permissions.IsAdminUser,)) # overrides IsAuthenticatedOrReadOnly
def delete(self, request, id):
instance = self.get_object() # ...
Надеюсь, это поможет кому-то столкнуться с той же проблемой!