如何不允许PUT方法但允许DRF ViewSet中的PATCH?

时间:2017-04-20 20:43:19

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

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()等),以便它工作正常吗?

5 个答案:

答案 0 :(得分:3)

而不是使用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()

答案 1 :(得分:1)

我认为一个优秀的解决方案是使用自定义router并禁用PUT的路由。然后使用自定义路由器作为视图集。

SimpleRouter

^路由器实现看起来像那样。因此,您只需要继承DefaultRouterroutes,并根据需要定义Route(mapping={...})类属性。您可以完全删除background: -webkit-linear-gradient(-45deg, #cccccc, #ffffff); -webkit-background-clip: text; -webkit-text-fill-color: transparent; 中'put'的映射,或者您可以定义自己的操作来处理它并返回适当的400-something共鸣。

答案 2 :(得分:1)

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

http_method_names

答案 3 :(得分:0)

类似于@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)

答案 4 :(得分:0)

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

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 中禁用它。