【问题标题】:detail_route on viewset not following object level permissionsviewset 上的 detail_route 不遵循对象级别权限
【发布时间】:2016-02-01 02:28:36
【问题描述】:

这是我的视图集:

class PageViewSet(viewsets.ModelViewSet):
    queryset = Page.objects.all()
    serializer_class = PageSerializer
    permission_classes = (IsAuthenticated, IsOwnerOrReadOnly,)

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user, location=self.request.user.userextended.location)

    @detail_route(methods=['post'])
    def add(self, request, pk=None):
        try:
            page = Page.objects.get(pk=pk)
        except:
            content = {'Page': ['The page you are trying to add no longer exists.']}
            return Response(content, status=status.HTTP_400_BAD_REQUEST)

        page.users.add(request.user)

        return Response(status=status.HTTP_204_NO_CONTENT)

这是我的IsOwnerOrReadOnly 权限:

class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    Allow only the owner (and admin) of the object to make changes (i.e. 
    do PUT, PATCH, DELETE and POST requests. A user is an
    owner of an object if the object has an attribute
    called owner and owner is == request.user. If the
    object is a User object or if the object does not have
    an owner attribute, then return object == request.user.
    """

    def has_permission(self, request, view):
        print('In permission')
        return True

    def has_object_permission(self, request, view, obj):
        print('In object level permission')
        if request.method in permissions.SAFE_METHODS:
            return True

        if request.user.is_staff:
            return True

        try:
            return obj.owner == request.user
        except: # if obj does not have an owner property (e.g. users don't

                # have owner properties).
            return obj == request.user

问题是,即使我不是页面的所有者,我也可以作为经过身份验证的用户发布到add-detail。当我发出帖子请求时,它只打印两次In permission,从不打印In object level permission。我的问题是,既然它是一个 detail_route 并且显然使用了一个 {lookup} 对象(请参阅此处,它表明它正在使用一个对象:http://www.django-rest-framework.org/api-guide/routers/),那么权限类中的 has_object_permission() 函数为什么没有被叫?为什么只调用常规的has_permission() 函数?

我希望有人链接到一个文档,该文档验证即使是 detail_route,也只有 has_permission 被调用。

编辑:这不是 Django rest framework ignores has_object_permission 的副本,因为我使用的是继承自 GenericAPIView 的 ModelViewSet(如文档中所述:http://www.django-rest-framework.org/api-guide/viewsets/#modelviewset)。

【问题讨论】:

  • @Ivan 这不是您链接到的帖子的副本,因为我使用的是继承自 GenericAPIView 的 ModelViewSet(我还编辑了我的帖子以添加此信息)。
  • 要点同在接受的答案中,由您在add中调用check_object_permissions。
  • @Ivan 答案是:“对象的权限检查由 DRF 在 APIView.check_object_permissions 方法中完成。由于您不使用 GenericAPIView,因此您定义了自己的 get_object 方法并且您拥有自己调用 check_object_permissions。”但是,我确实使用它(我从它继承)并且对于我所有其他 PageViewSet 请求(PUT、PATCH 和 DELETE),IsOwnerOrReadOnly 的 check_object_permissions 被调用得非常好。只是它不适用于我的 detail_route 函数。想知道它是否在文档中说 detail_route 不使用对象级权限?
  • 这就是我想说的:检查权限的不是路由器。当您定义要路由到的操作时,如果您不使用get_object,则由您调用check_object_permissions。我在您的add 中没有看到它。
  • 我会发布一个完整的答案。

标签: django-rest-framework


【解决方案1】:

见this answer。有一些区别,但要点是一样的:check_object_permissions 没有被调用。

虽然您是从ModelViewSet 继承的,但您并没有在您的add 方法中使用它的get_object 来检索页面。是get_object 调用check_object_permissions(不是路由器)进行检索、更新等操作,所以显然它不会被调用。

要修复它,请执行以下操作:

class PageViewSet(viewsets.ModelViewSet):

    # ...    

    @detail_route(methods=['post'])
    def add(self, request, pk=None):
        page = self.get_object()
        page.users.add(request.user)

        return Response(status=status.HTTP_204_NO_CONTENT)

或者只是在你的实现中的某个地方自己做self.check_object_permissions(page)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-10-20
    • 1970-01-01
    • 2012-06-14
    • 2019-12-24
    • 1970-01-01
    • 2017-07-12
    • 1970-01-01
    相关资源
    最近更新 更多