DRF 权限最佳实践 DRY

DRF permissions best practise DRY

目前在 DRF 中根据用户类型查看权限的最佳方法是什么? 在我的结构中有几个 user_types,例如 TEAM_LEADER 无法创建 team 对象但可以看到 teams 的列表。这意味着对于相同的 class 视图,我想对 POST 和 GET 使用不同的权限。 我希望尽可能干地做到这一点,并且我正在尝试遵循瘦视图胖模型设计原则(还想知道这是否是 2021 年遵循的良好做法)。

models.py 为用户模型


class User(AbstractBaseUser):
    ...fields here

    objects = UserManager()

    USERNAME_FIELD = "email"

    def __str__(self):
        return self.email

    def has_perm(self, perm, obj=None):
        if perm.Meta.verbose_name=="worksite" and perm.request.method =="POST":
            if self.user_type <= self.DEPARTMENT_MANAGER:
                return True
            else:
                return False
        return True

views.py


class DashboardPermissions(BasePermission):
    message="You dont have permission for this action"

    def has_permission(self, request, view):
        return request.user.has_perm(view.Meta.verbose_name)

class ViewName(CreateAPIView):
    permission_classes = (IsAuthenticated,DashboardPermissions)
    authentication_classes = ()
    serializer_class = WorksiteSerializer
    queryset = Worksite.objects.all()
    class Meta:
        verbose_name="view_name"

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

奖金问题我的解决方案会产生任何性能问题吗?

创建自定义 Permission class 是个好习惯。所以那部分对我来说看起来不错。我们可以讨论逻辑是应该在 Permission 中还是在 User 中(就像您所做的那样),但这没什么大不了的。

如果您想对视图中的不同端点拥有不同的权限,只需重写 get_permissions 方法即可。

# Inherited method from APIView
    def get_permissions(self):
        """
        Instantiates and returns the list of permissions that this view requires.
        """
        return [permission() for permission in self.permission_classes]

如您所见,对于所有服务,它将从 self.permission_classes 获取权限。

要在 GET/CREATE 之间使用不同的权限,您可以创建 endpoint: [...permissions]dict 并覆盖 get_permissions 以获取与当前操作匹配的权限

permissions = {
    "create": [P1, P2,],
    "get": [P1,]
}

def get_permissions(self):
    action = self.it_is_somewhere_in_there
    return [permission() for permissions in self.permissions[action]]

@JordanKowal 的回答是正确的,但如评论中所述,

Also then i'd be repeating the permissions dict a lot ? in order to do it for every class of view right

为此您可以创建一个 mixin class。它本质上允许您做的是将一些要在多个视图中复制的 code/feature 移动到一个独立的 class 并根据您的方便从它继承。

为了扩展 Jordan 的回答,mixin class 看起来像这样:

class DefaultPermissionsMixin(object):
    permissions = {
        "create": [IsAuthenticated, DashboardPermissions],
        "get": [DashboardPermissions]
    }
    
    def get_permissions(self):
        # default `get_permissions` method
        # reads `self.permission_classes`
        perms = super().get_permissions()
        if self.action in self.permissions.keys():
            return perms + [p() for p in self.permissions[self.action]]
        else:
            return perms
        
        
class View1(CreateAPIView, DefaultPermissionsMixin):
    # ...snip...

class View2(CreateAPIView, DefaultPermissionsMixin):
    # i can overwrite here per my convenience
    permissions = {
        "create": [DashboardPermissions],
        "delete": [],
    }
    # i can also define permissions the default way
    # that will be enabled on all actions
    permission_classes = [IsAuthenticated]
    # ...snip...