개요PUT vs. PATCHDRF에서의 PUT과 PATCH수정이 포함된 generic 구조UpdateModelMixin mixin의 update(), partital_update()Serializer 리소스 수정 방법ModelSerializer에서 update() 방식Serializer(, , partial=True)
이 포스트는 현재 작성중입니다
개요
해당 글에서는 DRF에서 수정 요청인 PUT과 PATCH 요청 시의 작동 과정, 그리고 PATCH 요청시 partital_update()가 어떤식으로 작동하는지를 중점적으로 다룬다.
PUT vs. PATCH
PUT과 PATCH 요청은 둘다 리소스 수정을 요청하는 메소드라는 공통점을 가지고 있다. 이들의 차이점은 PUT은 대상 리소스의 모든 속성을 한번에 수정한다는 것이고(모든 정보를 받아야함), PATCH는 일부 속성만 수정할 수 있다는 것이다.
자세한 내용은 다음 글에 설명되어 있다.
DRF에서의 PUT과 PATCH
DRF에서는
mixins.UpdateModelMixin
mixin을 포함한 generic(generics.UpdateAPIView
, generics.RetrieveUpdateAPIView
generics.RetrieveUpdateDestroyAPIView
)을 상속하여 View를 만들어 리소스 수정 기능을 간단히 추가할 수 있다.수정이 포함된 generic 구조
수정 기능만 들어간
UpdateAPIView
의 구조를 살펴보자.class UpdateAPIView(mixins.UpdateModelMixin, GenericAPIView): """ Concrete view for updating a model instance. """ def put(self, request, *args, **kwargs): return self.update(request, *args, **kwargs) def patch(self, request, *args, **kwargs): return self.partial_update(request, *args, **kwargs)
put과 patch 요청시 각각
self.update()
, self.partial_update()
를 호출한다. 위에서 언급했듯이 PATCH는 리소스 속성의 부분적인 수정을 허용하기에 partial_update()
라는 부분적인 업데이트를 의미하는 이름의 메소드를 호출하는 것을 볼 수 있다.UpdateModelMixin mixin의 update(), partital_update()
두 메소드는 다음과 같이 구현되어 있다.
class UpdateModelMixin: """ Update a model instance. """ def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) 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() def partial_update(self, request, *args, **kwargs): kwargs['partial'] = True return self.update(request, *args, **kwargs)
partital_update()
는 kwargs[’partital’]
을 True
로 설정한 뒤 update()
와 같이 처리되는 것을 확인할 수 있다.Serializer 리소스 수정 방법
호출된 update는 serializer 단계로 이어져 나간다.
Serializer에서 instance와 data를 둘다 제공한다면 기존에 존재하던 instance를 수정하는 방식으로 작동한다.
# https://www.django-rest-framework.org/api-guide/serializers/#saving-instances # .save() will update the existing `comment` instance. serializer = CommentSerializer(comment, data=data) # https://www.django-rest-framework.org/api-guide/serializers/#partial-updates # Update `comment` with partial data serializer = CommentSerializer(comment, data={'content': 'foo bar'}, partial=True)
ModelSerializer에서 update() 방식
DRF는 편의를 위해 모델을 기반으로 자동으로 Serializer를 설정해주는
ModelSerializer
를 제공한다.다음은
ModelSerializer
에서의 update()
메소드 구현체이다.class ModelSerializer(Serializer): def update(self, instance, validated_data): raise_errors_on_nested_writes('update', self, validated_data) info = model_meta.get_field_info(instance) # Simply set each attribute on the instance, and then save it. # Note that unlike `.create()` we don't need to treat many-to-many # relationships as being a special case. During updates we already # have an instance pk for the relationships to be associated with. m2m_fields = [] for attr, value in validated_data.items(): if attr in info.relations and info.relations[attr].to_many: m2m_fields.append((attr, value)) else: setattr(instance, attr, value) instance.save() # Note that many-to-many fields are set after updating instance. # Setting m2m fields triggers signals which could potentially change # updated instance and we do not want it to collide with .update() for attr, value in m2m_fields: field = getattr(instance, attr) field.set(value) return instance
여기서 핵심 로직은 첫번째 for문이다. 해당 for문에서는 m2m 필드가 아닌 경우에
setattr(instance, attr, value)
를 통해 필드를 찾아 지정해준다.