【问题标题】:How to combine/mix object level and user level permissions in DRF?如何在 DRF 中组合/混合对象级别和用户级别权限?
【发布时间】:2019-04-19 18:29:31
【问题描述】:

我目前正在处理一个 DRF 项目,其中管理员用户、教师用户和所有者用户应该能够访问对象详细信息视图。基本上所有用户类型,除了那些不是对象所有者或教师或管理员用户的用户。我能够为每个权限实现单独的权限,但是当我需要在视图上组合这些权限时,我遇到了障碍,因为在对象级别权限之前检查了用户级别权限。因此我不能使用布尔操作数来组合它们,我必须为我的视图编写这些丑陋的权限类。

我的问题是:

如何以更简洁的方式在我的详细视图上实现这些权限,或者,是否有更简洁的方式来获取我的结果?

如您所见,我违反了 DRY,因为我有 IsAdminOrOwner 和 IsAdminOrTeacherOrOwner 权限。在我看来,您还会注意到我覆盖了 get_permissions() 以便为相应的请求方法拥有适当的权限类。欢迎任何关于这个和其他实现的 cmets,我想要批评,以便我可以改进它。

下面是permissions.py:

from rest_framework import permissions
from rest_framework.permissions import IsAdminUser

class IsOwner(permissions.BasePermission):

    def has_permission(self, request, view):
        return True

    def has_object_permission(self, request, view, obj):
        return request.user == obj

class IsTeacher(permissions.BasePermission):

    def has_permission(self, request, view):
        return request.user.groups.filter(
            name='teacher_group').exists()

class IsAdminOrOwner(permissions.BasePermission):

    def has_object_permission(self, *args):
        is_owner = IsOwner().has_object_permission(*args)

        #convert tuple to list
        new_args = list(args)
        #remove object for non-object permission args
        new_args.pop()
        is_admin = IsAdminUser().has_permission(*new_args)

        return is_owner or is_admin

class IsAdminOrTeacherOrOwner(permissions.BasePermission):

    def has_object_permission(self, *args):
        is_owner = IsOwner().has_object_permission(*args)

        #convert tuple to list
        new_args = list(args)
        #remove object for non-object permission args
        new_args.pop()
        is_admin = IsAdminUser().has_permission(*new_args)
        is_teacher = IsTeacher().has_permission(*new_args)

        return is_admin or is_teacher or is_owner

以下是我的观点:

class UserRetrieveUpdateView(APIView):
    serializer_class = UserSerializer

    def get_permissions(self):
        #the = is essential, because with each view
        #it resets the permission classes
        #if we did not implement it, the permissions
        #would have added up incorrectly
        self.permission_classes = [IsAuthenticated]

        if self.request.method == 'GET':
            self.permission_classes.append(
                IsAdminOrTeacherOrOwner)

        elif self.request.method == 'PUT':
            self.permission_classes.append(IsOwner)

        return super().get_permissions()

    def get_object(self, pk):
        try:
            user = User.objects.get(pk=pk)
            self.check_object_permissions(self.request, user)
            return user
        except User.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        user = self.get_object(pk)
        serializer = self.serializer_class(user)
        return Response(serializer.data, status.HTTP_200_OK)

    def put(self, request, pk, format=None):
        user = self.get_object(pk)
        #we use partial to update only certain values
        serializer = self.serializer_class(user,
            data=request.data, partial=True)

        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data,
                status.HTTP_200_OK)

        return Response(status=status.HTTP_400_BAD_REQUEST)

【问题讨论】:

  • shoten 代码提示:既然您只是使用getput 操作,为什么不使用RetrieveUpdateAPIView 而不是APIView。这样你就不需要get_object,你不必重写getput方法。 RetrieveUpdateAPIView 中的所有内容都已存在
  • 感谢您的提示。我一定会调查的。关于权限,您知道更好的方法吗?

标签: python django django-rest-framework django-permissions


【解决方案1】:

Permissions in DRF 可以使用按位或运算符组合。例如,您可以这样做:

permission_classes = (IsAdmin | IsOwner | IsTeacher)

这样,您不必定义单独的类来组合它们。

【讨论】:

  • 您好,首先,感谢您的回复!但是,我知道可以使用按位运算符这一事实,但由于 IsOwner 是对象级别权限,其余权限是用户级别,因此按位不适用于它们,我的测试失败:(
  • @DJN 我不明白这有什么不同。 (IsAdminUser | IsOwner) 应该实现与您的 IsAdminOrOwner 相同的功能。
  • 不,它没有,因为 IsAdminUser 在 IsOwner 权限之前经过测试,因为 IsOwner perm 是对象级别,而 IsAdminUser 不是。
  • 但这正是您的IsAdminOrOwner 所做的。首先测试哪个并不重要,因为它是或。最后,你做了return is_owner or is_admin,所以如果is_owner 为假而is_admin 为真,那么它仍然会通过。无论如何,我真的看不出这两者之间有什么区别
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-29
  • 1970-01-01
  • 2014-07-11
  • 2013-03-25
  • 2012-12-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多