52

是否有最佳实践为给定APIView或的每个操作分配不同的权限ViewSet

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

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

4

5 回答 5

82

在 DRF 文档中,

注意:实例级别的 has_object_permission 方法只有在视图级别的 has_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

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,)

对于 Django 2.0 替换is_authenticated()is_authenticated. 该方法已变成一个属性。

于 2015-12-08T18:01:31.473 回答
18

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

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

看看提供的IsAuthenticatedOrReadOnly类(和其他类),看看它是多么容易的一个很好的例子。

我希望这会有所帮助。

于 2013-10-11T08:35:16.403 回答
9

我个人很讨厌弗兰肯斯坦这种怪物般的自定义权限,在我看来,当涉及到 Django 框架时,它并不是很地道。

所以我想出了以下解决方案——它@list_route@detail_route装饰器的工作方式非常相似。我们依赖于方法/函数是一流对象的事实

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

装饰器.py

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

如您所见,它向它装饰的函数添加了一个字典,并使用作为 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()

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

get_serializer_class被调用时,它会做同样的事情并serializerroute_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)

对于我们明确定义的自定义路由,我们可以@route_action_arguments在方法上明确设置。

就通用视图集和方法而言,我们仍然可以使用 @method_decorator

@method_decorator(route_action_arguments(serializer=LoginSerializer), name='create')
class UserViewSet (RouteActionArgumentsMixin, RequestContextMixin, viewsets.ModelViewSet):
于 2016-12-21T22:57:45.933 回答
3

Django 有一个名为的权限类DjangoObjectPermissions,它使用 Django Guardian 作为身份验证后端。

当您在设置中激活 Django 监护人时,您只需将其添加permission_classes = [DjandoObjectPermissions]到您的视图中,它会自动进行权限验证,因此您可以根据对特定django.contrib.auth组或用户设置的权限进行“CRUD”。

请参阅带有示例的要点。

您可以将Django Guardian设置为您的身份验证支持

于 2013-10-15T21:22:25.813 回答
2

RestFramework 的基于类的视图对每个 HTTP 动词都有方法(即:HTTP GET =>view.get()等)。您只需要使用django.contrib.auth文档中的权限、用户、组和装饰器。

于 2013-10-11T08:21:06.247 回答