Подтвердить что ты не робот

Django REST Framework - отдельные разрешения для каждого метода

Я пишу API с использованием Django REST Framework, и мне интересно, можно ли указать разрешения для метода при использовании представлений на основе классов.

Чтение документации Я вижу, что это довольно легко сделать, если вы пишете представления на основе функций, просто используя декоратор @permission_classes над функцией представлений, которые вы хотите защитить с помощью разрешений. Однако я не вижу способа сделать то же самое при использовании CBV с классом APIView, потому что тогда я указываю разрешения для полного класса с атрибутом permission_classes, но тогда он будет применен ко всем методам класса. (get, post, put...).

Итак, возможно ли иметь представления API, написанные с использованием CBV, а также указывать различные разрешения для каждого метода класса представления?

4b9b3361

Ответ 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()  # ...

Надеюсь, это поможет кому-то столкнуться с той же проблемой!