【问题标题】:Disable a method in a ViewSet, django-rest-framework禁用 ViewSet 中的方法,django-rest-framework
【发布时间】:2014-07-01 14:11:24
【问题描述】:

ViewSets 有自动的方法来列出、检索、创建、更新、删除……

我想禁用其中的一些,我想出的解决方案可能不是一个好的解决方案,因为OPTIONS 仍然声明这些是允许的。

知道如何以正确的方式做到这一点吗?

class SampleViewSet(viewsets.ModelViewSet):
    queryset = api_models.Sample.objects.all()
    serializer_class = api_serializers.SampleSerializer

    def list(self, request):
        return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)
    def create(self, request):
        return Response(status=status.HTTP_405_METHOD_NOT_ALLOWED)

【问题讨论】:

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


【解决方案1】:

ModelViewSet的定义是:

class ModelViewSet(mixins.CreateModelMixin, 
                   mixins.RetrieveModelMixin, 
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet)

与其扩展ModelViewSet,不如直接使用你需要的东西?比如:

from rest_framework import viewsets, mixins

class SampleViewSet(mixins.RetrieveModelMixin,
                    mixins.UpdateModelMixin,
                    mixins.DestroyModelMixin,
                    viewsets.GenericViewSet):
    ...

使用这种方法,路由器应该只为包含的方法生成路由。

参考

ModelViewSet

【讨论】:

  • @SunnySydeUp 现在尝试一下,似乎路由器确实为列表视图生成了路由,但它是 404s,因为 ViewSet 不知道如何处理请求。这是您的预期吗?
  • 通过只使用你需要的 mixins,你可以禁用 GET、POST、PUT、DELETE 方法,但是如果你使用路由器,我无法找到如何禁用 PATCH 方法。
  • @MuneebAhmad PATCH 方法从UpdateModelMixin 启用。如果你想使用更新而不是补丁,我目前可以想到两种方法。您可以覆盖视图中允许的方法并删除“补丁”,也可以覆盖partial_update 方法并调用http_method_not_allowed(request, *args, **kwargs)。我还没有测试过,所以我不确定它是否有效
  • @JulioMarins 我添加了一个参考。我不确定这是否是你想要的。
  • 如果有人想制作只读视图集,那么他们可以使用class SampleViewSet(viewsets.ReadOnlyModelViewSet)
【解决方案2】:

您可以继续使用 viewsets.ModelViewSet 并在您的 ViewSet 上定义 http_method_names

例子

class SampleViewSet(viewsets.ModelViewSet):
    queryset = api_models.Sample.objects.all()
    serializer_class = api_serializers.SampleSerializer
    http_method_names = ['get', 'post', 'head']

添加http_method_names 后,您将无法再添加putpatch

如果你想要put而不想要patch,你可以保留http_method_names = ['get', 'post', 'head', 'put']

在内部,DRF 视图从 Django CBV 扩展而来。 Django CBV 有一个名为 http_method_names 的属性。所以你也可以在 DRF 视图中使用 http_method_names。

[Shameless Plug]:如果这个答案有帮助,你会喜欢我在 DRF 上的系列帖子https://www.agiliq.com/blog/2019/04/drf-polls/

【讨论】:

  • 这种方式的问题是无法禁用列表或检索。必须禁用两者或都不禁用
  • 这对我不起作用,在包含 get 和 head 之后,我仍然能够发帖
  • 这适用于我在 django 1.9 上。很好的解决方案。用户可以通过其他方式执行 GET 请求是否存在风险?
  • 很棒的解决方案。在 python3Django 1.10 上工作就好了。
  • 我更喜欢这种方法,因为我无法更改 mixins 的继承以包含 PATCH,但不能更改 PUT,因为它们都是 mixins.UpdateModelMixin 的实现
【解决方案3】:

虽然写了这么久,突然发现其实有办法禁用这些功能,可以直接在views.py中编辑。

来源:https://www.django-rest-framework.org/api-guide/viewsets/#viewset-actions

from rest_framework import viewsets, status
from rest_framework.response import Response

class NameThisClassWhateverYouWantViewSet(viewsets.ModelViewSet):

    def create(self, request):
        response = {'message': 'Create function is not offered in this path.'}
        return Response(response, status=status.HTTP_403_FORBIDDEN)

    def update(self, request, pk=None):
        response = {'message': 'Update function is not offered in this path.'}
        return Response(response, status=status.HTTP_403_FORBIDDEN)

    def partial_update(self, request, pk=None):
        response = {'message': 'Update function is not offered in this path.'}
        return Response(response, status=status.HTTP_403_FORBIDDEN)

    def destroy(self, request, pk=None):
        response = {'message': 'Delete function is not offered in this path.'}
        return Response(response, status=status.HTTP_403_FORBIDDEN)

【讨论】:

  • 这应该是比较好的方式。
  • 我认为如果 HTTP_400_BAD_REQUEST 与身份验证无关,这里会更合适。
  • HTTP_405_METHOD_NOT_ALLOWED 呢?
  • 同意@Ryan
  • 我还建议使用raise MethodNotAllowed(request.method)
【解决方案4】:

如果您尝试从 DRF 视图中禁用 PUT 方法,您可以创建自定义路由器:

from rest_framework.routers import DefaultRouter

class NoPutRouter(DefaultRouter):
    """
    Router class that disables the PUT method.
    """
    def get_method_map(self, viewset, method_map):

        bound_methods = super().get_method_map(viewset, method_map)

        if 'put' in bound_methods.keys():
            del bound_methods['put']

        return bound_methods

通过在路由器上禁用该方法,您的 api 架构文档将是正确的。

【讨论】:

  • 由于部分补丁未在 DRF 中正确实施,明智的做法是按照此处描述的方式全局删除它
【解决方案5】:

我喜欢@pymen answer 的想法,但他的实施没有奏效。这样做:

class SampleViewSet(viewsets.ModelViewSet):
    http_method_names = [m for m in viewsets.ModelViewSet.http_method_names if m not in ['delete']]

这样做的优点是只做排除和简单。虽然它看起来有点 hacky,但如果它只用于那个 ViewSet,它可能正是您所需要的。

【讨论】:

  • 这是唯一对我有用的解决方案,以防仅阻止方法 put 而不是 patch。
【解决方案6】:

如何在 DRF 中禁用 ViewSet 的“DELETE”方法

class YourViewSet(viewsets.ModelViewSet):
    def _allowed_methods(self):
        return [m for m in super(YourViewSet, self)._allowed_methods() if m not in ['DELETE']]

附注这比显式指定所有必要的方法更可靠,因此忘记一些重要方法 OPTIONS、HEAD 等的机会更少

P.P.S. 默认情况下,DRF 有 http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

【讨论】:

  • 你不能在课堂上调用super,没有self
【解决方案7】:

在 Django Rest Framework 3.x.x 中,您可以通过将字典传递给 as_view 方法来简单地启用您希望为 ModelViewSet 启用的每个方法。在此字典中,键必须包含请求类型(GET、POST、DELETE 等),值必须包含相应的方法名称(列表、检索、更新等)。例如,假设您希望创建或读取 Sample 模型,但您不希望对其进行修改。所以这意味着您希望启用listretrievecreate 方法(并且您希望禁用其他方法。)

您需要做的就是将路径添加到urlpatterns,如下所示:

path('sample/', SampleViewSet.as_view({
    'get': 'list',
    'post': 'create'
})),
path('sample/<pk>/', SampleViewSet.as_view({  # for get sample by id.
    'get': 'retrieve'
}))

如您所见,上述路由设置中没有 deleteput 请求,因此例如,如果您向 url 发送 put 请求,它会以 405 Method Not Allowed 回复您:

{
    "detail": "Method \"PUT\" not allowed."
}

【讨论】:

    【解决方案8】:

    禁用视图集上的方法、在 api 中保持一致并返回有用的错误消息的最直接方法是简单地为您不想使用的任何方法引发 MethodNotAllowed 异常。对于 GET 之类的映射为检索和列出禁用列表的方法,您可以自定义错误消息以指示 GET 仅适用于 URL 上的查找值。

    from rest_framework.exceptions import MethodNotAllowed
    
    class SampleViewSet(viewsets.ModelViewSet):
        queryset = api_models.Sample.objects.all()
        serializer_class = api_serializers.SampleSerializer
    
    def list(self, request):
        raise MethodNotAllowed('GET', detail='Method "GET" not allowed without lookup')
    def create(self, request):
        raise MethodNotAllowed(method='POST')
    

    这将返回 405 状态码和 DRF 使用格式的 json 数据:

    {'detail': 'Method "POST" not allowed.'}
    

    【讨论】:

    • 如果我想应用补丁,我将无法做到。
    【解决方案9】:

    如果您打算禁用 put/post/destroy 方法,您可以使用

    viewsets.ReadOnlyModelViewSet https://www.django-rest-framework.org/tutorial/6-viewsets-and-routers/#refactoring-to-use-viewsets

    【讨论】:

      猜你喜欢
      • 2016-09-04
      • 2014-05-29
      • 1970-01-01
      • 2016-06-28
      • 1970-01-01
      • 2018-10-01
      • 2020-06-07
      • 1970-01-01
      • 2020-08-26
      相关资源
      最近更新 更多