ViewSet方法的Django rest框架permission_classes
Posted
技术标签:
【中文标题】ViewSet方法的Django rest框架permission_classes【英文标题】:Django rest framework permission_classes of ViewSet method 【发布时间】:2016-06-28 12:19:15 【问题描述】:我正在使用 Django REST 框架编写一个 REST API,我想通过权限保护某些端点。权限类看起来像是提供了一种优雅的方式来实现这一点。我的问题是我想为不同的重写 ViewSet 方法使用不同的权限类。
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
def create(self, request, *args, **kwargs):
return super(UserViewSet, self).create(request, *args, **kwargs)
@decorators.permission_classes(permissions.IsAdminUser)
def list(self, request, *args, **kwargs):
return super(UserViewSet, self).list(request, *args, **kwargs)
在上面的代码中,我也想允许未经身份验证的用户注册(创建用户),但我不想让任何人都可以列出用户,只为员工。
在docs 中,我看到了使用permission_classes
装饰器保护API 视图(不是ViewSet 方法)的示例,并且我看到了为整个ViewSet 设置权限类。但它似乎不适用于覆盖的 ViewSet 方法。有没有办法只将它们用于某些端点?
【问题讨论】:
@ilse2005 最终,我使用了github.com/Tivix/django-rest-auth,但你用一个很好的解决方案回答了这些问题 【参考方案1】:我认为没有内置的解决方案。但是您可以通过覆盖get_permissions
方法来实现这一点:
from rest_framework.permissions import AllowAny, IsAdminUser
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes_by_action = 'create': [AllowAny],
'list': [IsAdminUser]
def create(self, request, *args, **kwargs):
return super(UserViewSet, self).create(request, *args, **kwargs)
def list(self, request, *args, **kwargs):
return super(UserViewSet, self).list(request, *args, **kwargs)
def get_permissions(self):
try:
# return permission_classes depending on `action`
return [permission() for permission in self.permission_classes_by_action[self.action]]
except KeyError:
# action is not set return default permission_classes
return [permission() for permission in self.permission_classes]
【讨论】:
我认为将get_permissions
放入 Mixin 是个好主意。【参考方案2】:
我创建了一个派生自@ilse2005 答案的超类。在所有后续的 django 视图中你可以继承这个来实现动作级别的权限控制。
class MixedPermissionModelViewSet(viewsets.ModelViewSet):
'''
Mixed permission base model allowing for action level
permission control. Subclasses may define their permissions
by creating a 'permission_classes_by_action' variable.
Example:
permission_classes_by_action = 'list': [AllowAny],
'create': [IsAdminUser]
'''
permission_classes_by_action =
def get_permissions(self):
try:
# return permission_classes depending on `action`
return [permission() for permission in self.permission_classes_by_action[self.action]]
except KeyError:
# action is not set return default permission_classes
return [permission() for permission in self.permission_classes]
【讨论】:
【参考方案3】:我可能迟到了,但正如其中一位评论者指出的那样,我使用了 mixin。从@Itachi 那里得到答案,这是我的 mixin 实现:
class ViewSetActionPermissionMixin:
def get_permissions(self):
"""Return the permission classes based on action.
Look for permission classes in a dict mapping action to
permission classes array, ie.:
class MyViewSet(ViewSetActionPermissionMixin, ViewSet):
...
permission_classes = [AllowAny]
permission_action_classes =
'list': [IsAuthenticated]
'create': [IsAdminUser]
'my_action': [MyCustomPermission]
@action(...)
def my_action:
...
If there is no action in the dict mapping, then the default
permission_classes is returned. If a custom action has its
permission_classes defined in the action decorator, then that
supercedes the value defined in the dict mapping.
"""
try:
return [
permission()
for permission in self.permission_action_classes[self.action]
]
except KeyError:
if self.action:
action_func = getattr(self, self.action, )
action_func_kwargs = getattr(action_func, "kwargs", )
permission_classes = action_func_kwargs.get(
"permission_classes"
)
else:
permission_classes = None
return [
permission()
for permission in (
permission_classes or self.permission_classes
)
]
下面是 mixin 的使用方法:
class MyViewSet(ViewSetActionPermissionMixin, ModelViewSet):
...
permission_action_classes =
"list": [AllowAny],
"create": [IsAdminUser],
"custom_action": [MyCustomPermission],
@action(...)
def custom_action(self, request, *args, **kwargs):
...
【讨论】:
【参考方案4】:我认为所有其他答案都很棒,但我们不应该直接取消在装饰器中定义的默认操作 permission_classes
。所以,
from rest_framework import viewsets
from rest_framework import permissions
class BaseModelViewSet(viewsets.ModelViewSet):
queryset = ''
serializer_class = ''
permission_classes = (permissions.AllowAny,)
# Refer to https://***.com/a/35987077/1677041
permission_classes_by_action =
'create': permission_classes,
'list': permission_classes,
'retrieve': permission_classes,
'update': permission_classes,
'destroy': permission_classes,
def get_permissions(self):
try:
return [permission() for permission in self.permission_classes_by_action[self.action]]
except KeyError:
if self.action:
action_func = getattr(self, self.action, )
action_func_kwargs = getattr(action_func, 'kwargs', )
permission_classes = action_func_kwargs.get('permission_classes')
else:
permission_classes = None
return [permission() for permission in (permission_classes or self.permission_classes)]
现在我们可以用这两种方式定义permission_classes
。由于我们在超类中定义了默认的全局 permission_classes_by_action
,因此我们可以为选项 2 中的所有操作删除该定义。
class EntityViewSet(BaseModelViewSet):
"""EntityViewSet"""
queryset = Entity.objects.all()
serializer_class = EntitySerializer
permission_classes_by_action =
'create': (permissions.IsAdminUser,),
'list': (permissions.IsAuthenticatedOrReadOnly,),
'retrieve': (permissions.AllowAny,),
'update': (permissions.AllowAny,),
'destroy': (permissions.IsAdminUser,),
'search': (permissions.IsAuthenticated,) # <--- Option 1
@action(detail=False, methods=['post'], permission_classes=(permissions.IsAuthenticated,)) # <--- Option 2
def search(self, request, format=None):
pass
【讨论】:
以上是关于ViewSet方法的Django rest框架permission_classes的主要内容,如果未能解决你的问题,请参考以下文章
django-rest-framework框架总结之View视图之APIViewGenericAPIView视图集ViewSet
如何在 django rest 框架中反转 ViewSet 自定义操作的 URL
Django REST框架--viewSets & Routers
Django REST Framework Viewset 返回 404(GET 请求)