【问题标题】:Django DRF: Restricting PUT options in ViewSetDjango DRF:限制 ViewSet 中的 PUT 选项
【发布时间】:2021-04-09 12:04:32
【问题描述】:

我想限制我的 ViewSet 以便它允许基于权限的不同输入:

我的模型:

class Link(models.Model):
    name = models.CharField("name", max_length = 128)
    page = models.PositiveIntegerField("page", default = 1, choices = [(1, 1), (2, 2), (3, 3)])
    layouts = models.ManyToManyField(Layout)

class Layout(models.Model):
    name = models.CharField("name", max_length = 32) 

序列化器:

class LinkSerializer(serializers.ModelSerializer):
    test = serializers.SerializerMethodField("get_layouts")

    def get_layouts(self): ## this is not displayed?
        return "test" 

    class Meta:
        model = Link
        fields = ["name", "page", "test"]

例如,我有一个提供all_pages 权限的助手类。在restframework API中,我现在有一个来自第1-3页的下拉菜单,但是如果用户无权使用所有页面,我只想显示page 1以供选择(或使其成为没有输入字段的文本字段) . 同样的问题适用于 ManyToMany 字段:如何将布局下拉选项限制为我在管理平面中为该用户选择的选项?

现在我手动检查ViewSetPUT 方法中的所有内容并返回“不允许用户... -> 错误请求”。

编辑:BC 有人问我,现在我在做这样的事情:

class TestViewSet(viewsets.ViewSet):
    serializer_class = LinkSerializer
    http_method_names = ['get', 'put', 'head']
    authentication_classes = [SessionAuthentication,]
    permission_classes = [IsAuthenticated,]

    def list(self, request):
    ...

    def put(self, request):
        ## page logic
        if "app.all_pages" in request.user.get_user_permissions():
            try:
                if not 1 < int(request.data["page"]) <= 3:
                    return Response(data = "page not 2 or 3", 
                                    status = status.HTTP_406_NOT_ACCEPTABLE)
            except Exception as E:
                return Response(data = f"""Error in page selection: {E}""", 
                                status = status.HTTP_400_BAD_REQUEST)
            page = int(request.data["page"])
        else:
            page = 2
        
        ## layout logic:
        store = Store.objects.get(user = request.user)
        allowed_layouts = [l.id for l in store.layouts.all()]
        if not int(request.data["layout"]) in allowed_layouts:
            return Response(data = f"""layout not allowed for store {store}""", 
                            status = status.HTTP_406_NOT_ACCEPTABLE)
        layout = Layout.objects.get(pk = request.data["layout"])
        
        ## do something with page 2 and layout ... in DB/another API   

【问题讨论】:

  • 发布与逻辑相关的视图集。也发布你的辅助方法
  • 我通过我的解决方法更新了我的问题(这很好,但我确信这是一个非常次优的解决方案)。

标签: django django-rest-framework


【解决方案1】:

这个问题有点不清楚,所以我将解释权限的工作原理,您将自己使用它们。

第 1 页

DRF 中的权限仅作为 2 种方法的包装器,这是它的工作原理。

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

    def has_object_permission(self, request, view, obj)
        return True

这是一个权限,has_permission 说(request.user 有权访问 当前view?) 和has_object_permission 表示(request.user 有权对obj 采取行动)。

obj 是视图内被操作的对象。如果您的视图适用于用户,则该对象将是 User 的一个实例。

这与permission_classes 一起使用

    permission_classes = [IsAuthenticated, PermissionX]

第 2 页

您通常使用user.has_perm(perm) 而不是'name' in user.permissions。这是因为权限可以存在于user.groups 而不仅仅是user.permissions

第 3 页

引发的任何异常都应该是 DRF 中存在的 PermissionDenied 类型。

第 4 页

在您当前应用的 permission.py 文件夹中提取您的权限。

第 5 页

您的权限不会集成到 django 管理员界面中。这是因为它没有使用 DRF,也没有使用您的端点。它正在使用 django 为其提供的东西。这很正常,我们正在讨论的逻辑将由您的 API 本身强制执行,当您创建自定义仪表板时,您也需要在前端将其作为一种 UX 强制执行。

【讨论】:

  • 第一:感谢您的详细回答。赞赏!但它不包含我的问题的一个非常重要的观点:例如,如果用户 (A) 登录并看到第 1 - 3 页的下拉列表,我怎样才能让用户 (B) (谁没有权限查看所有页面)在他登录时显示一个只有一个(第 1 页)条目的菜单?还是我认为这一切都是错误的,只是“显示所有内容并在 perm 被拒绝时引发异常”?
  • 页面本身可能是有权限的模型
  • 是的,到目前为止我就是这样做的。我只是在想,“隐藏”用户无论如何都无法访问的字段/选项可能会更好。
  • 是的,最好为此创建一个动态序列化程序(已记录)
  • 这正是我的问题所针对的。您能否展示如何处理我在原始问题中遇到的“页面”案例?
猜你喜欢
  • 1970-01-01
  • 2019-03-03
  • 1970-01-01
  • 1970-01-01
  • 2021-08-07
  • 1970-01-01
  • 1970-01-01
  • 2019-06-11
  • 1970-01-01
相关资源
最近更新 更多