[drf] advanced serializer usage

nikevapormax·2022년 12월 22일
0

TIL

목록 보기
104/116
post-custom-banner

Django REST framework 공식문서

상황

  • 프론트 단의 요청에 의해 하나의 모델 안에 들어있던 필드들을 각 종류에 맞게 세 가지로 묶어(depth를 하나 늘려서) 데이터를 주고 받는 케이스가 생겼었다.
  • 그냥 GET 요청만 사용한다면 serializers.SerializerMethodField를 사용해 데이터를 간단하게 묶어서 보여줄 수 있었지만, 해당 app에는 GET, POST, PUT 요청이 존재했다.
  • 따라서 serializer를 묶을 필드별로 생성해 적용했다.
class CareSerializer(serializers.Serializer):
    care_over_30 = serializers.IntegerField(label="...")
    care_over_60 = serializers.IntegerField()
    care_over_90 = serializers.IntegerField()
    care_over_120 = serializers.IntegerField()
    care_over_150 = serializers.IntegerField()
    care_over_180 = serializers.IntegerField()
    care_over_210 = serializers.IntegerField()
    care_over_240 = serializers.IntegerField()
    care_over_270 = serializers.IntegerField()


class BathSerializer(serializers.Serializer):
    bath_car_under_60 = serializers.IntegerField()
    bath_car_bath_under_60 = serializers.IntegerField()
    bath_under_60 = serializers.IntegerField()
    bath_car_over_60 = serializers.IntegerField()
    bath_car_bath_over_60 = serializers.IntegerField()
    bath_over_60 = serializers.IntegerField()


class NursingSerializer(serializers.Serializer):
    nursing_under_30 = serializers.IntegerField()
    nursing_under_60 = serializers.IntegerField()
    nursing_over_60 = serializers.IntegerField()


class EmployeeRecipientSalarySerializer(serializers.ModelSerializer):
    recipient = RecipientSerializer()
    care = CareSerializer(label="...")
    bath = BathSerializer()
    nursing = NursingSerializer()
    
    class Meta:
        model = 모델
        fields = [...]
  • 여기서 문제가 발생했다. POST를 통해 데이터 생성은 가능했지만, 생성된 데이터를 조회하려 하니 데이터가 나오지 않았다.

해결

  • .to_representation(self, instance)를 사용해 저장된 데이터를 보여주고, .to_internal_value(self, data)를 사용해 들어온 데이터를 가공해 데이터베이스에 저장해 주었다.
    def to_representation(self, instance):
        ret = super().to_representation(
            {
                **instance.__dict__,
                "recipient": instance.recipient,
                "care": CareSerializer(instance).data,
                "bath": BathSerializer(instance).data,
                "nursing": NursingSerializer(instance).data,
            }
        )
        ret.update()
        return ret

    def to_internal_value(self, data):
        ret = super().to_internal_value(data)
        ret.update(
            {
                **ret.pop("care"),
                **ret.pop("bath"),
                **ret.pop("nursing"),
            }
        )
        return ret

생각

  • 사실 위에 나온 작업을 할 필요는 전혀 없었다. 굳이 하나의 모델에 있는 필드들을 세 depth를 추가적으로 만들어 묶는 것은 그다지 효율적이지 못하다고 생각한다.
  • 하지만 이 과정을 거치며 drf의 내장 함수 오버라이딩을 해볼 수 있다는 점이 좋았다.
  • 지금까지 내가 validate() 함수 안에서 했던 작업들도 .to_internal_value(self, data)에서 하는 것이 좋을 수도 있다는 점 또한 알게 되었다.
    • 결국 validate() 함수 안에서는 진짜 유효성 검증만 하고, url path나 query parameter에서 가져오는 값은 .to_internal_value(self, data)에서 가져와 유효성 검증을 하는 것이다.
profile
https://github.com/nikevapormax
post-custom-banner

0개의 댓글