[ Django ] SerializerMethodField를 이용한 시리얼라이저 커스텀하기
🌈 프로그래밍/Django

[ Django ] SerializerMethodField를 이용한 시리얼라이저 커스텀하기

반응형

 

이번 포스팅에서는 Django에서 Serializer에 대한 내용을 다뤄보려고 합니다.

한동안 포스팅을 올려야지만 생각하고 실제로는 못하고 있었네요 😂

아무리 바쁘더라도 기록은 필수..!

 

그러면 내용에 들어가기 앞서 왜 SerializerMethodField를 사용해야 하는지,

무엇을 하려고에 대한 내용을 정리해보려고 합니다!

 

우선, 현재 환자와 의사에 관련된 진료 예약 서비스를 구축하고 있습니다.

환자는 원하는 시간 특정 의사에게 진료를 받고 싶다고 요청을 보냅니다.

그러면 요청을 받은 의사는 목록을 확인하여 진료 시간을 확인하고, 해당 요청을 시간 내에 수락할 수 있도록 하는 서비스입니다.

 

간단한 설명을 마치고 바로 본론으로 들어가보도록 하겠습니다.

def get(self, request, *args, **kwargs):
    doctor_id = request.GET.get("id", None)
    self._is_valid_request_ids(None, doctor_id)

    results = (
        self.get_queryset()
        .select_related("patient")
        .filter(doctor=doctor_id, is_booked=False)
    )

    serializer = self.get_serializer(results, many=True)
    return Response(serializer.data, status=status.HTTP_200_OK)

위의 함수는 의사의 id를 query parameter로 받아온 후, 해당 의사가 요청받은 모든 데이터들을 리스트 하는 함수입니다.

좀 더 자세히 살펴보도록 하죠. SQL을 어떻게 만들어내는지 알아보도록 하겠습니다.

# print(results.query)

SELECT `care`.`id`, `care`.`patient_id`, `care`.`doctor_id`, `care`.`book_time`, `care`.`created_at`, `care`.`expire_time`, `care`.`is_booked`, `patient`.`id`, `patient`.`name`
FROM `care`
INNER JOIN `patient`
ON (`care`.`patient_id` = `patient`.`id`)
WHERE (`care`.`doctor_id` = 1 AND NOT `care`.`is_booked`);

모델의 테이블 명을 care로 지어주었기 때문에 care.필드 형태입니다.

또한, select_related로 환자 테이블을 정참조 하여 INNER JOIN을 하는 모습입니다.

select_related와 prefected_related는 이후 좀 더 자세하게 다루도록 하겠습니다.

위 SQL 문장을 실행한 결과는 아래와 같습니다.

 

따라서 요청에 대한 응답 데이터는 아래와 같습니다.

[
	// 생략
    {
        "id": 17,
        "book_time": "2022-07-22T14:40:00",
        "created_at": "2022-07-21T13:39:24.495377",
        "expire_time": "2022-07-21T13:59:24.485285",
        "is_booked": false,
        "patient": 1,
        "doctor": 1
    }
]

위와 같이 응답이 온 이유로는 serializer_class와 연결된 model이 어떤 형태를 띠냐에 따라서 결정됩니다.

결과를 잘 살펴보면, 어차피 모든 데이터들은 예약이 되지 않은 데이터이기 때문에, 필요 없는 필드(is_booked)가 있고

의사 ID값은 출력될 필요가 없어보이고(어처피 특정 의사에 대한 데이터이므로),

환자 ID만 출력되고 있는데 이름으로 바꿔주어 응답 데이터를 만들고 싶네요!

 

우선 model부터 살펴보도록 합시다.

class CareRequestList(models.Model):
    patient = models.ForeignKey(
        Patient, on_delete=models.CASCADE, verbose_name="환자 id"
    )
    doctor = models.ForeignKey(
        Doctor, on_delete=models.CASCADE, verbose_name="의사 id"
    )
    book_time = models.DateTimeField(verbose_name="희망 진료 시각")
    created_at = models.DateTimeField(
        auto_now_add=True, verbose_name="예약 요청 시각"
    )
    expire_time = models.DateTimeField(verbose_name="요청 만료 시각")
    is_booked = models.BooleanField(default=False, verbose_name="진료 요청 수락 여부")

    class Meta:
        db_table = "care"

모델은 위처럼 되어있고, patient와 doctor는 외래 키로 연결되어 있는 상태입니다.

serializer는 아래와 같이 모델의 모든 필드로 설정해주었습니다.

class CareRequestSerializer(serializers.ModelSerializer):

    class Meta:
        model = CareRequestList
        fields = "__all__"

따라서 앞서 본 결과 데이터(JSON)가 출력된 것입니다.

 

하지만, 지금 하려는 것은 결과 데이터의 값들 중, 환자 ID에 해당하는 환자 테이블에 실제 이름을 결과 데이터로 추가해주려고 합니다.

그렇다면.. 어떻게 해야 할까요?

바로 제목에서 이미 말씀드렸다시피 SerializerMethodField를 사용하여 커스텀하게 필드를 구성할 수 있습니다.

이미 실행된 SQL 문장에서 보았듯이 결과 테이블에 환자 이름이 같이 출력되는 것을 알 수 있습니다.

따라서 시리얼라이저를 아래와 같이 수정해줍시다.

class CareRequestListSerializer(serializers.ModelSerializer):
    patient_name = serializers.SerializerMethodField()

    def get_patient_name(self, obj):
        return obj.patient.name

    class Meta:
        model = CareRequestList
        fields = [
            "id",
            "patient_name",
            "book_time",
            "expire_time",
        ]

다른 점으로는 새로운 필드(patient_name)이라는 필드를 SerializerMethodField를 사용해 추가해 주었고,

get_patient_name이라는 함수를 통해 환자의 이름을 가져오도록 합니다.

그리고 필드에는 출력될 정보만 담아주도록 합시다.

View에서는 serializer를 받아오는 부분을 수정된 serializer로만 변경해주면 되겠죠?

그러면 응답을 확인해보도록 하겠습니다.

[
    // 생략
    {
        "id": 17,
        "patient_name": "김환자",
        "book_time": "2022-07-22T14:40:00",
        "expire_time": "2022-07-21T13:59:24.485285"
    }
]

위처럼 원하는 데이터만 그리고 SerializerMethodField로 환자의 이름까지 잘 들어간 형태로 응답 결과가 만들어졌습니다 👍

CareRequestListSerializer

이상으로 커스텀하게 시리얼라이저를 사용하는 방법에 대해서 알아보았습니다.

감사합니다!

반응형