在ViewSet中禁用一个方法,django-rest-framework

191

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)

参见:https://dev59.com/qVcP5IYBdhLWcg3w_ex2#69648046 - phoenix
13个回答

358

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

更新: 在DRF 3.14.0中,使用未在混合类中实现的方法会返回405 - 方法不允许

Method Not Allowed: /status/
[06/Mar/2023 01:03:01] "POST /status/ HTTP/1.1" 405 41

1
@SunnySydeUp 我现在正在尝试这个,似乎路由器确实为列表视图生成了路由,但它会返回404,因为ViewSet不知道如何处理请求。这是您所期望的吗? - Steve Jalim
3
只需使用你需要的 mixins,就可以禁用 GET、POST、PUT、DELETE 方法,但我无法找到如何特别禁用 PATCH 方法,特别是如果你正在使用路由器。 - Muneeb Ahmad
4
PATCH方法是从UpdateModelMixin启用的。如果您想使用更新而不是补丁,我目前可以想到两种方法。您可以在视图中覆盖允许使用的方法并删除“patch”,或者您可以覆盖partial_update方法并调用http_method_not_allowed(request, *args, **kwargs)。我没有测试过,所以不确定它是否有效。 - SunnySydeUp
1
@JulioMarins 我已经添加了一个引用。不过我不确定这是否是你想要的。 - SunnySydeUp
2
如果有人想创建只读视图集,则可以使用class SampleViewSet(viewsets.ReadOnlyModelViewSet) - Bikash kharel
显示剩余5条评论

240
你可以继续使用 viewsets.ModelViewSet 并在你的视图集上定义 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。

[无耻广告]:如果这个答案有帮助,你会喜欢我在https://www.agiliq.com/blog/2019/04/drf-polls/上关于DRF的系列文章。


43
这种方法的问题在于无法禁用列表或检索功能。必须同时禁用它们或全部启用。 - Fuad
5
这种方法对我无效,即使添加了GET和HEAD方法,我仍然可以进行POST请求。 - RunLoop
这在我的Django 1.9上有效。很棒的解决方案。用户还有其他方式可以进行GET请求吗? - Ycon
1
很棒的解决方案。在python3Django 1.10上都可以完美运行。 - Urda
5
我喜欢这种方法,因为我无法更改混合继承以包含PATCH但不包含PUT,因为它们都是 mixins.UpdateModelMixin 的实现。 - ThatsAMorais
这是我的理想解决方案,因为它可以更新自动生成的API文档页面中的HTTP方法 :) - greenie-beans

35

虽然这篇文章已经有一段时间了,但我突然发现实际上有一种方法可以禁用这些功能,你可以直接在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)

1
这应该是一个更可取的方式。 - digitake
1
如果与身份验证无关,我认为使用HTTP_400_BAD_REQUEST会更加恰当。 - steamdragon
12
HTTP_405_METHOD_NOT_ALLOWED 是什么意思? - Ryan
同意 @Ryan - Jay Soni
8
我建议使用raise MethodNotAllowed(request.method) - Ryan
如果你禁用了 update 方法,那么你基本上也禁用了 partial_update,因为它在最后调用了 update - FooBar

10

如果你想在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中未正确实现部分补丁,因此最好按照此处描述的方式全局删除它。 - oden
bound_methods.pop('put', None) 更加简洁。 - Ryan

6
我喜欢 @pymen 的答案思路,但是他的实现方式并没有起作用。这个可以:
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方法的情况下找到的唯一解决方案。 - Elias Prado

4
这是我喜欢的。
from rest_framework import viewsets, mixins

class ContentViewSet(mixins.CreateModelMixin, viewsets.ReadOnlyModelViewSet):
    queryset = models.Content.objects.all()
    serializer_class = serializers.ContentSerializer

ReadOnlyModelViewSet - 只允许只读方法。这些方法基本上是“GET”和“OPTIONS”请求。

CreateModelMixin - 只允许创建新的元素。这是“POST”请求。

以上示例中禁用了所有其他方法,例如“PUT”,“PATH”和“DELETE”。您可以根据自己的要求使用不同的mixins来启用各种方法。


4

禁用视图集上的方法、保持api中的一致性并返回有用的错误信息最简单的方法是,对于任何您不想使用的方法,只需引发MethodNotAllowed异常即可。对于像GET这样映射到retrieve和list的方法,其中list被禁用,您可以自定义错误消息以指示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.'}

如果我想应用一个补丁,我将无法这样做。 - Elias Prado

3
如何在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等)的几率较小。

另外,DRF默认具有http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']


你不能在类级别上调用 super,因为没有 self - validname

3
在Django Rest Framework 3.x.x中,您可以通过向as_view方法传递一个字典来简单地启用您想要在ModelViewSet中启用的每个方法。在这个字典中,键必须包含请求类型(GET、POST、DELETE等),值必须包含相应的方法名称(list、retrieve、update等)。例如,假设您想创建或读取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请求,所以例如当您发送一个put请求到该url时,会响应一个405 Method Not Allowed错误:

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

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接