【问题标题】:How to Not allow the PUT method at all but allow PATCH in a DRF ViewSet?如何根本不允许 PUT 方法但允许在 DRF ViewSet 中使用 PATCH?
【发布时间】:2017-09-17 16:18:58
【问题描述】:

PUTPATCH 都是同一个 mixin 的一部分(UpdateModelMixin)。

所以如果我像这样扩展它:

class UserViewSet(mixins.UpdateModelMixin, GenericViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

PUTPATCH 都是允许的。我不想让PUT 用于我的应用程序(因为PATCH 已经完成了这项工作,我想仅使用POST 来限制对象创建)。一种方法是创建权限:

class NoPut(permissions.BasePermission):
    """
    PUT not allowed.
    """
    message = 'You do not have permission to complete the action you are trying to perform.'

    def has_object_permission(self, request, view, obj):
        if view.action == "update":
            return False
        return True

并将此权限授予我所有允许 PATCH 的 ViewSet。这是最好的方法吗?有没有更优选的方式?

编辑:查看@wim 提供的答案后,这是一个很好的解决方案吗(除了put 的映射被删除之外,一切都保持不变):

from rest_framework.routers import SimpleRouter
class NoPutRouter(SimpleRouter):

    routes = [
        # List route.
        Route(
            url=r'^{prefix}{trailing_slash}$',
            mapping={
                'get': 'list',
                'post': 'create'
            },
            name='{basename}-list',
            initkwargs={'suffix': 'List'}
        ),
        # Dynamically generated list routes.
        # Generated using @list_route decorator
        # on methods of the viewset.
        DynamicListRoute(
            url=r'^{prefix}/{methodname}{trailing_slash}$',
            name='{basename}-{methodnamehyphen}',
            initkwargs={}
        ),
        # Detail route.
        Route(
            url=r'^{prefix}/{lookup}{trailing_slash}$',
            mapping={
                'get': 'retrieve',
                 # put removed
                'patch': 'partial_update',
                'delete': 'destroy'
            },
            name='{basename}-detail',
            initkwargs={'suffix': 'Instance'}
        ),
        # Dynamically generated detail routes.
        # Generated using @detail_route decorator on methods of the viewset.
        DynamicDetailRoute(
            url=r'^{prefix}/{lookup}/{methodname}{trailing_slash}$',
            name='{basename}-{methodnamehyphen}',
            initkwargs={}
        ),
    ]

或者我需要重新定义SimpleRoute 中的其他方法(例如__init()__get_routes()_get_dynamic_routes()get_method_map() 等)以使其正常工作吗?

【问题讨论】:

    标签: django-rest-framework django-rest-viewsets


    【解决方案1】:

    不要使用mixins.UpdateModelMixin,只需定义自己的mixin,它只会执行补丁:

    class UpdateModelMixin(object):
        """
        Update a model instance.
        """
        def partial_update(self, request, *args, **kwargs):
            partial = True
            instance = self.get_object()
            serializer = self.get_serializer(instance, data=request.data, partial=partial)
            serializer.is_valid(raise_exception=True)
            self.perform_update(serializer)
    
            if getattr(instance, '_prefetched_objects_cache', None):
                # If 'prefetch_related' has been applied to a queryset, we need to
                # forcibly invalidate the prefetch cache on the instance.
                instance._prefetched_objects_cache = {}
    
            return Response(serializer.data)
    
        def perform_update(self, serializer):
            serializer.save()
    

    【讨论】:

      【解决方案2】:

      如果你想使用内置的mixins.UpdateModelMixin,限制为PATCH并禁止swagger显示PUT你可以使用http_method_names

      class UserViewSet(mixins.UpdateModelMixin, GenericViewSet):
          queryset = User.objects.all()
          serializer_class = UserSerializer
          http_method_names = ["patch"]
      

      【讨论】:

        【解决方案3】:

        我认为更好的解决方案是使用自定义 router 并禁用 PUT 路由。然后将您的自定义路由器用于视图集。

        class SimpleRouter(BaseRouter):
            routes = [
                # List route.
                Route(
                    url=r'^{prefix}{trailing_slash}$',
                    mapping={
                        'get': 'list',
                        'post': 'create'
                    },
                    name='{basename}-list',
                    initkwargs={'suffix': 'List'}
                ),
                # Dynamically generated list routes.
                # Generated using @list_route decorator
                # on methods of the viewset.
                DynamicListRoute(
                    url=r'^{prefix}/{methodname}{trailing_slash}$',
                    name='{basename}-{methodnamehyphen}',
                    initkwargs={}
                ),
                # Detail route.
                Route(
                    url=r'^{prefix}/{lookup}{trailing_slash}$',
                    mapping={
                        'get': 'retrieve',
                        'put': 'update',
                        'patch': 'partial_update',
                        'delete': 'destroy'
                    },
                    name='{basename}-detail',
                    initkwargs={'suffix': 'Instance'}
                ),
                # Dynamically generated detail routes.
                # Generated using @detail_route decorator on methods of the viewset.
                DynamicDetailRoute(
                    url=r'^{prefix}/{lookup}/{methodname}{trailing_slash}$',
                    name='{basename}-{methodnamehyphen}',
                    initkwargs={}
                ),
            ]
        

        ^ 路由器实现看起来像这样。所以你只需要继承SimpleRouter,或者DefaultRouter,并定义你想要的routes类属性。您可以完全删除Route(mapping={...}) 中“放置”的映射,或者您可以定义自己的操作来处理它并返回适当的 400-something 响应。

        【讨论】:

        • 谢谢。好的,所以我编辑了答案以包含新的自定义路由器。您能否验证它是一个正常的自定义路由器,还是我需要在SimpleRouter 中重新定义其他方法才能使其正常工作?
        【解决方案4】:

        类似于@linovia's answer,但使用标准的mixin:

        from rest_framework.exceptions import MethodNotAllowed
        
        class UpdateModelMixin(mixins.UpdateModelMixin, viewsets.GenericViewSet):
            """
            update:
                Update Model
            """
        
            def update(self, *args, **kwargs):
                raise MethodNotAllowed("POST", detail="Use PATCH")
        
            def partial_update(self, request, *args, **kwargs):
                # Override Partial Update Code if desired
                return super().update(*args, **kwargs, partial=True)
        

        【讨论】:

        • 这行得通,但是如何禁止 swagger 显示 PUT 端点?
        • @TNT 对于drf-spectacular,您可以使用@extend_schema_view(update=extend_schema(exclude=True))
        【解决方案5】:

        这是我正在使用的解决方案:

        class SomeViewSet(
            mixins.UpdateModelMixin,
            ...
        ):
            @swagger_auto_schema(auto_schema=None)
            def update(self, request, *args, **kwargs):
                """Disabled full update functionality"""
                partial = kwargs.get('partial', False)  # This must be .get() not .pop()
                if not partial:
                    raise exceptions.MethodNotAllowed(request.method)
        
                return super(SomeViewSet, self).update(request, *args, **kwargs)
        

        这也会在 drf-yasg UI 中禁用它。

        【讨论】:

          【解决方案6】:

          类似于@EbramShehata 的解决方案,但适用于drf-spectacular (OpenAPI 3)。这将禁止完全更新 (PUT),并将其从生成的 OpenAPI 3 架构中排除。

          class SomeViewSet(
              mixins.UpdateModelMixin,
              ...
          ):
              @extend_schema(exclude=True)
              def update(self, request: Request, *args: Any, **kwargs: Any) -> Response:
                  """Disallow full update (PUT) and allow partial update (PATCH)."""
                  if kwargs.get("partial", False):  # Use .get() instead of .pop()
                      return super().update(request, args, kwargs)
          
                  raise MethodNotAllowed(request.method)
          

          【讨论】:

            【解决方案7】:

            简单直接的方法:

            class UserViewSet(viewsets.ModelViewSet):
                queryset = User.objects.all()
                serializer_class = UserSerializer
                http_method_names = ['get', 'post', 'patch'] # <---------
            

            这样PUT 方法将不被允许。

            【讨论】:

            • 这应该是公认的答案。它是如此简单
            猜你喜欢
            • 1970-01-01
            • 2015-10-31
            • 1970-01-01
            • 2017-08-29
            • 2018-11-12
            • 1970-01-01
            • 1970-01-01
            • 2023-03-23
            • 1970-01-01
            相关资源
            最近更新 更多