has_object_permission 未调用

has_object_permission not called

我查看了关于同一主题的类似问题,我认为我遵守了为 has_object_permission 指定的所有规则。

这是我的设置。

REST_FRAMEWORK = {
    
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
        'users.permissions.CanAccessData', # this is my custom class
    ],    
    ... 
}

这是我的许可class

class CanAccessData(permissions.BasePermission):
    message = 'You do not have permission to perform this action.'

    def has_permission(self, request, view):
        print "has_permission`"
        return True

    def has_object_permission(self, request, view, obj):
        print "has_object_permission"
        return False

这是我的视图结构:

class CompleteList(generics.ListCreateAPIView):
    permission_classes = (CanAccessData,)
    serializer_class = SomeSerializer
    model = Some
    filter_backends = (filters.OrderingFilter, filters.SearchFilter)
    ordering_fields = (tuple of Some fields)
    search_fields = ordering_fields
    ordering = ('-create_date')

仍然,has_object_permission 没有被调用,但是 has_permission 被调用了。

列表视图不调用has_object_permissiondocumentation 表示如下:

Also note that the generic views will only check the object-level permissions for views that retrieve a single model instance. If you require object-level filtering of list views, you'll need to filter the queryset separately. See the filtering documentation for more details.

我运行陷入同样的​​问题。 has_object_permission 函数在列出对象时从不调用。

即使以下可能不是最有效的解决方案,您也可以按如下方式在您的视图中覆盖 list 方法,这就是我为我解决的方法:

from typing import List
import rest_framework.permissions as drf_permissions

def list(self, request, *args, **kwargs):

    # base query set
    queryset: QuerySet = self.filter_queryset(self.get_queryset())

    # check object permissions for each object individually
    valid_pks: List[int] = []  # # storage for keys of valid objects
    permissions: List[drf_permissions.BasePermission] = self.get_permissions()
    for obj in queryset:
        for permission in permissions:
            if permission.has_object_permission(request, self, obj):
                valid_pks.append(obj.pk)

    # remove not valid objects from the queryset
    queryset = queryset.filter(pk__in=valid_pks)

    # ... business as usual (original code)
    page = self.paginate_queryset(queryset)
    if page is not None:
        serializer = self.get_serializer(page, many=True)
        return self.get_paginated_response(serializer.data)

    serializer = self.get_serializer(queryset, many=True)

    return Response(serializer.data)

除了预取相关对象并单独检查权限外,这基本上是原始实现。

但是,从某种意义上说,这可能是 DRY,因为您不必重写 get_queryset() 方法来以某种方式重新发明您的 has_object_permission 逻辑。但它也很慢,因为它获取对象两次。不过,您可以通过使用已经预取的对象而不是过滤查询集来改善这种情况。

has_object_permission 方法只有在视图级 has_permission 检查已经通过时才会被调用。

看看这个真实世界的例子

奥林匹亚项目 github (https://github.com/mozilla/addons-server/blob/master/src/olympia/ratings/permissions.py)

class CanDeleteRatingPermission(BasePermission):
  """A DRF permission class wrapping user_can_delete_rating()."""

    def has_permission(self, request, view):
        return request.user.is_authenticated

    def has_object_permission(self, request, view, obj):
        return user_can_delete_rating(request, obj)

当使用此权限class时,如果has_permission通过则has_object_permission( user_can_delete_rating() 函数 ) 被调用。