Python 每个动作权限的 Django 休息框架

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19313314/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 13:25:12  来源:igfitidea点击:

Django rest-framework per action permission

pythondjangodjango-rest-frameworkdjango-permissions

提问by lWhitmore

I'm a newbie in developing with Django + Django Rest-framework and I'm working on a project that provides REST Api access. I was wondering what is the best practice to assign a different permission to each action of a given ApiView or Viewset.

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

Let's suppose I defined some permissions classes such as 'IsAdmin', 'IsRole1', 'IsRole2', ..., and I want to grant different permissions to the single actions (e.g. a user with Role1 can create or retrieve, a user with Role2 can update, and only an Admin can delete).

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

How can I structure a class based view in order to assign a permission class to the 'create', 'list', 'retrieve', 'update', 'delete' actions? I'm trying to do so to have a class that can be reused for different tables that have the same permission pattern.

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

Maybe I'm just drowning in an inch of water, thank you for your replies.

可能我只是淹死在一寸水里,谢谢你的回复。

采纳答案by Carlton Gibson

You can create a custom permission classextending DRF's BasePermission.

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

You implement has_permissionwhere you have access to the requestand viewobjects. You can check request.userfor the appropriate role and return True/Falseas appropriate.

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

Have a look at the provided IsAuthenticatedOrReadOnlyclass (and others) for a good example of how easy it is.

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

I hope that helps.

我希望这有帮助。

回答by bruno desthuilliers

RestFramework's class-based views have methods for each HTTP verb (ie : HTTP GET => view.get() etc). You just have to use django.contrib.auth's permissions, users, groups and decorators as documented.

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

回答by Carlton Gibson

Django has a persmissions class called DjangoObjectPermissions which uses Django Guardian as an authentication backend.

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

When you have Django guardian active in your settings you just add permission_classes = [DjandoObjectPermissions]to your view and it does permission authentication automatically, so you can 'CRUD' based on the permission set to a particular django.contrib.authgroup or user.

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

See a gistwith an example.

查看带有示例的要点

You can set Django Guardian as your authentication backed http://django-guardian.readthedocs.org/en/latest/installation.html

您可以将 Django Guardian 设置为您的身份验证支持http://django-guardian.readthedocs.org/en/latest/installation.html

回答by Chemical Programmer

In DRF documentation,

在 DRF 文档中,

Note: The instance-level has_object_permission method will only be called if the view-level has_permission checks have already passed

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

Let's assume following permission about userobject

让我们假设以下关于user对象的权限

  • List : staff only
  • Create : anyone
  • Retrieve : own self or staff
  • Update, Partial update : own self or staff
  • Destroy : staff only
  • 名单:仅限员工
  • 创建:任何人
  • 检索:自己或员工
  • 更新,部分更新:自己或员工
  • 销毁:仅限员工


permissons.py

权限.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

视图.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,)

EDIT

编辑

For Django 2.0 replace is_authenticated()with is_authenticated. The method has been turned into an attribute.

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

回答by Marcin Kudzia

I personally hate this kind of frankenmonster custom permissions, in my opinion, it's not very idiomatic when it comes to Django framework; So I came up with the following solution - it's very similar to how @list_routeand @detail_routedecorators work. We are relying on the fact that the methods/functions are first class objects

我个人很讨厌这种 frankenmonster 自定义权限,在我看来,对于 Django 框架来说,这不是很习惯;所以我想出了以下解决方案 - 它@list_route@detail_route装饰器的工作方式非常相似。我们依赖于方法/函数是一流对象这一事实

First of all I'm creating such decorator:

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

decorators.py

装饰器.py

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

As you can see it adds a dictionary to the function it decorates with parameters passed as arg list

正如你所看到的,它向函数中添加了一个字典,它使用作为 arg 列表传递的参数进行装饰

Now I created such mixin: mixins.py

现在我创建了这样的 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 does two things; when get_permissionsis called, it checks which 'action' is executed, and looksup the permission_classes collection from the route_action_kwargsassociated with the viewset.action_method.route_action_kwargs

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

when get_serializer_classis called, it does the same and picks the serializerfrom route_action_kwargs

get_serializer_class被调用时,它会做同样的事情并serializerroute_action_kwargs

Now the way we can use it:

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

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

For custom routs we define explicitly we can just set the @route_action_argumentsexplicitly on the method.

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

In terms of generic viewsets and methods, we can still add them using the @method_decorator

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

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