Django rest-framework每个动作权限

lWh*_*ore 31 python django django-permissions django-rest-framework

我是使用Django + Django Rest-framework进行开发的新手,我正在开发一个提供REST Api访问的项目.我想知道为给定的ApiView或Viewset的每个动作分配不同的权限是什么最佳做法.

假设我定义了一些权限类,例如'IsAdmin','IsRole1','IsRole2',...,我想为单个操作授予不同的权限(例如,具有Role1的用户可以创建或检索用户, Role2可以更新,只有管理员可以删除).

如何构建基于类的视图,以便为"创建","列表","检索","更新","删除"操作分配权限类?我试图这样做有一个类可以重用于具有相同权限模式的不同表.

也许我只是淹死在一寸水里,谢谢你的回复.

Che*_*mer 50

在DRF文档中,

注意:只有在视图级别has_permission检查已通过时才会调用实例级别的has_object_permission方法

让我们假设关于user对象的以下许可

  • 清单:仅限员工
  • 创造:任何人
  • 检索:自己或员工
  • 更新,部分更新:拥有自己或员工
  • 毁灭:仅限员工

permissons.py

from rest_framework import permissions

class UserPermission(permissions.BasePermission):

    def has_permission(self, request, view):
        if view.action == 'list':
            return request.user.is_authenticated() and request.user.is_admin
        elif view.action == 'create':
            return True
        elif view.action in ['retrieve', 'update', 'partial_update', 'destroy']:
            return True
        else:
            return False

    def has_object_permission(self, request, view, obj):
        # Deny actions on objects if the user is not authenticated
        if not request.user.is_authenticated():
            return False

        if view.action == 'retrieve':
            return obj == request.user or request.user.is_admin
        elif view.action in ['update', 'partial_update']:
            return obj == request.user or request.user.is_admin
        elif view.action == 'destroy':
            return request.user.is_admin
        else:
            return False
Run Code Online (Sandbox Code Playgroud)

views.py

from .models import User
from .permissions import UserPermission
from .serializers import UserSerializer
from rest_framework import viewsets


class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = (UserPermission,)
Run Code Online (Sandbox Code Playgroud)

编辑

Django的2.0替换is_authenticated()is_authenticated.该方法已变为属性.

  • `view.action`似乎比`request.method`更直观.+1 (5认同)

Car*_*son 16

您可以创建扩展DRF的自定义权限类BasePermission.

您可以has_permission在有权访问requestview对象的位置实现.您可以检查request.user适当的角色并根据需要返回True/ False.

看看提供的IsAuthenticatedOrReadOnly类(以及其他类),看看它是多么容易的好例子.

我希望有所帮助.


Lau*_*det 14

DRF 文档中,您现在可以这样做:

装饰器允许您覆盖任何视图集级别的配置,例如permission_classes、serializer_class、filter_backends...:

@action(detail=True, methods=['post'], permission_classes=[IsAdminOrIsSelf])
def set_password(self, request, pk=None):
Run Code Online (Sandbox Code Playgroud)


小智 5

我个人讨厌这种Frankenmonster自定义权限,在我看来,在Django框架方面并不是很习惯。因此,我提出了以下解决方案-它@list_route@detail_route装饰器的工作方式非常相似。我们依靠这样的事实,即方法/函数是一流的对象

首先,我要创建这样的装饰器:

decorators.py

def route_action_arguments(**kwargs):
    """
    Add arguments to the action method
    """
    def decorator(func):
        func.route_action_kwargs = kwargs
        return func
    return decorator
Run Code Online (Sandbox Code Playgroud)

如您所见,它向其函数添加了一个字典,该字典使用作为arg列表传递的参数进行装饰

现在,我创建了这样的mixin: mixins.py

class RouteActionArgumentsMixin (object):
    """
    Use action specific parameters to 
    provide:
    - serializer
    - permissions
    """

    def _get_kwargs(self):
        action = getattr(self, 'action')
        if not action:
            raise AttributeError
        print('getting route kwargs for action:' + action)
        action_method = getattr(self, action)
        kwargs = getattr(action_method, 'route_action_kwargs')
        print(dir(kwargs))
        return kwargs

    def get_serializer_class(self):
        try:
            kwargs = self._get_kwargs()
            return kwargs['serializer']
        except (KeyError, AttributeError):
            return super(RouteActionArgumentsMixin, self).get_serializer_class()

    def get_permissions(self):
        try:
            kwargs = self._get_kwargs()
            return kwargs['permission_classes']
        except (KeyError, AttributeError):
            return super(RouteActionArgumentsMixin, self).get_permissions()
Run Code Online (Sandbox Code Playgroud)

Mixin做两件事;get_permissions调用when时,它将检查执行了哪个“动作”,并从route_action_kwargsviewset.action_method.route_action_kwargs

get_serializer_class被调用时,它执行相同的操作并serializer从中选择route_action_kwargs

现在我们可以使用它的方式:

@method_decorator(route_action_arguments(serializer=LoginSerializer), name='create')
class UserViewSet (RouteActionArgumentsMixin, RequestContextMixin, viewsets.ModelViewSet):
    """
    User and profile managment viewset
    """

    queryset = User.objects.all()
    serializer_class = UserSerializer

    @list_route(methods=['post'])
    @route_action_arguments(permission_classes=(AllowAny,), serializer=LoginSerializer)
    def login(self, request):
        serializer = self.get_serializer_class()(data=request.data)
Run Code Online (Sandbox Code Playgroud)

对于自定义路由,我们可以显式定义,只需@route_action_arguments在方法上显式设置即可。

在通用视图集和方法方面,我们仍然可以使用 @method_decorator

@method_decorator(route_action_arguments(serializer=LoginSerializer), name='create')
class UserViewSet (RouteActionArgumentsMixin, RequestContextMixin, viewsets.ModelViewSet):
Run Code Online (Sandbox Code Playgroud)