【DRF】PUT 메소드를 POST 메소드 대신 사용

Django REST Framework에서 PUT 메소드로 레코드 (인스턴스)를 만들고 싶다면



Django REST Framework(DRF)에 한하지 않고, REST API에서는 통상, HTTP 프로토콜의 POST 메소드로 인스턴스(=레코드)를 작성해 PUT 메소드로 갱신을 하지만, PUT 로 작성하는 것은 할 수 없는지 조사해 보았는데 방법이 있었으므로 비망록으로서 남겨 둔다.

일어난 문제와 하고 싶은 일


PUT를 사용하여 레코드 업데이트 (예 : 사용자가 한 번 작성한 레코드의 데이터를 편집 한 경우)를 사용했지만 DB에 존재하지 않는 레코드를 PUT로 보내려고하면 404 했다. 보통은 POST 로 레코드로 작성하려고 하고 있지만, PUT 에서도 레코드를 작성할 수 있도록 하고 싶다.

다시 한번, 설명한다고 상정하고 있는 거동은 다음과 같다.

[ 이렇게 했으면 좋겠다 ]
  • 통상은 POST 로 레코드를 신규 작성해, PUT 인가 PATCH (부분 갱신)로 레코드의 갱신
  • PUT 에서 업데이트하려고 시도한 레코드가 DB에 존재하지 않으면 404 오류없이 새로 생성됩니다.

  • POST나 PUT, PATCH의 차이를 잘 모르는 경우는 PUT, POST 또는 PATCH?

    해결책



    공식 튜토리얼의 PUT as create 에 해결법이 실렸다.

    Prior to version 3.0 the REST framework mixins treated PUT as either an update or a create operation, depending on if the object already existed or not.

    Allowing PUT as create operations is problematic, as it necessarily exposes information about the existence or non-existence of objects. It's also not obvious that transparently allowing re-creating of previously deleted instances is necessarily a better default behavior tes simp.

    Both styles "PUT as 404"and "PUT as create"can be valid in different circumstances, but from version 3.0 onwards we now use 404 behavior as the default, due to it being simpler and more obvious.

    If you need to generic PUT-as-create behavior you may want to include something like this AllowPUTAsCreateMixin class as a mixin to your views.

    요점은, 「DRF 버젼 3.0 이상에서는 PUT 로 존재하지 않는 레코드를 갱신하려고 하면 404 스테이터스가 되돌아오게 되어 있지만 와 같이 레코드를 신규 작성할 수 있게 된다」라고 하는 것 같다.

    상기 링크처의 스니펫과 같은 것이지만, 필요한 모듈의 임포트문이 빠져 있었기 때문에 더한 것을 실어 둔다. 이것을 임포트해 레코드 갱신에 사용하고 있는 View 클래스에 Mixin 하면 PUT 에서도 레코드 작성을 할 수 있게 된다. 프런트 엔드에서 조건 분기로 POST 메소드와 PUT 메소드를 구분하는 경우 등에 POST 의 메소드만으로 작성과 갱신의 양쪽 모두를 끝낼 수 있으므로, 코드가 간결하게 될 것 같다

    put_as_create.py
    from django.http import Http404
    from rest_framework import status
    from rest_framework.response import Response
    from rest_framework.request import clone_request
    
    
    class AllowPUTAsCreateMixin(object):
        """
        The following mixin class may be used in order to support PUT-as-create
        behavior for incoming requests.
        """
    
        def update(self, request, *args, **kwargs):
            partial = kwargs.pop('partial', False)
            instance = self.get_object_or_none()
            serializer = self.get_serializer(instance, data=request.data, partial=partial)
            serializer.is_valid(raise_exception=True)
    
            if instance is None:
                lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
                lookup_value = self.kwargs[lookup_url_kwarg]
                extra_kwargs = {self.lookup_field: lookup_value}
                serializer.save(**extra_kwargs)
                return Response(serializer.data, status=status.HTTP_201_CREATED)
    
            serializer.save()
            return Response(serializer.data)
    
        def partial_update(self, request, *args, **kwargs):
            kwargs['partial'] = True
            return self.update(request, *args, **kwargs)
    
        def get_object_or_none(self):
            try:
                return self.get_object()
            except Http404:
                if self.request.method == 'PUT':
                    # For PUT-as-create operation, we need to ensure that we have
                    # relevant permissions, as if this was a POST request.  This
                    # will either raise a PermissionDenied exception, or simply
                    # return None.
                    self.check_permissions(clone_request(self.request, 'POST'))
                else:
                    # PATCH requests where the object does not exist should still
                    # return a 404 response.
                    raise
    
    

    좋은 웹페이지 즐겨찾기