생각과 고민이 담긴 코드

Nested Serializer - Django Rest Framework 본문

Django

Nested Serializer - Django Rest Framework

0_Hun 2021. 10. 19. 22:09

 

현재 공모전에서 모바일 애플리케이션을 개발하면서 DRF를 활용하여 REST API를 만들고 있다.

하나의 Reivew에 여러 Tag들이 들어가는 기능을 개발하고 있고

그에 따라 Review 테이블은 Tag 테이블과 1:N 관계를 갖는다.

 

다음은 그렇게 구현된 Model의 코드이다.

models.py의 일부

위 코드를 보면 1:N 관계를 정의하기 위해 Tag 테이블 쪽에 review의 id를 FK로 정의한 것을 알 수 있다.

 

 

그런데 상황에 따라서 각 테이블의 CRUD API를 각각 만들 수 없는 경우가 존재한다.

예를 들어, 한 화면에서 모든 리뷰들을 불러올 때 각 리뷰들의 태그들도 한 번에 보여줘야 할 때나

리뷰를 생성할 때 태그들도 한 번에 생성시켜야 할 때는 related realation들의 data들을 한 번에 처리해줘야 할 필요성이 있다.

 

그러한 상황들을 처리하기 위해 DRF에서는 Nested Serializer 기능을 제공한다.

말이 어려워 보이지만 JSON 데이터 형식을 떠올리면 이해가 쉽다.

 

{
    "content": "용기가 좀 작았네.",
    "store":2,
    "user": 6,
    "tag": [
        {
            "tag_content": "크레이프케익",
            "type": 1,
            "color_index": 150000
        },
        {
            "tag_content": "560ml",
            "type": 2,
            "color_index": 150000
        }
    ]
}

위와 같이 프론트엔드에서 JSON 데이터를 구성해서 보낸다고 생각해보자.

가장 상위에 있는 데이터들은 review 테이블에 들어가는 데이터들이고

Tag data는 "tag" 하위의 리스트 형태로 나열되어있다.  이러한 형태를 Nested(중첩된) 형태라고 하고

Nested Serializer는 이러한 중첩된 JSON 데이터를 1:N 테이블 구조로 인식하여

각각 테이블에 알맞게 데이터를 deserialize 시켜 DB에 넣어준다.

 

 

serializers.py의 일부

그렇다면 이제 Nested Serializer의 사용법을 알아보자. 먼저 각 테이블에 관한 serializer class를 각각 만들어준다.

Review 테이블, Tag 테이블, ReviewImg 테이블이 있다면

ReviewSerializer, TagSerializer, ReviewImgSerializer들을 각각 만들어줘야 한다.

참고로 여기서 ReviewImg 테이블도 Tag 테이블과 마찬가지로 Review 테이블과 1:N 관계를 이룬다.

 

그런 다음 ReviewSerializer 내에서 TagSerializer와 ReviewImgSerializer를 변수에 할당시켜서

fields에 명시해놓으면 된다. 이렇게 하면 GET을 통해 아래와 같이 여러 테이블의 중첩된 데이터를 반환받을 수 있다.

 

GET Reviews의 Response

 

그렇다면 중첩된 JSON 데이터를 받아서 Nested Serializer을 활용하여 데이터를 저장시키려면 어떻게 해야 할까?

아까 봤던 코드를 다시 봐보면 ReviewImgSerializer에는 인자로 "read_only = True" 넣어준 것을 볼 수 있다.

즉, ReviewImgSerializer는 GET을 통한 Read만 가능하게 한다는 뜻이고 TagSerializer는 생성, 수정, 삭제도 가능하게 하려면 해당 인자는 빼줘야 한다.

 

 

serializer.py의 일부

그리고 생성을 가능하게 하기 위해선 추가적으로 ReviewSerialzier 내에서 create() 함수를 오버라이드 해야 한다.

공식문서에서 나온 대로 따라서 구현했으며 참고 링크는 포스팅 마지막에 남겨 놓았다.

코드를 간단히 보면 입력으로 받은 dictionary 객체(받은 json 데이터를 파싱 한 것)에서 'tag' key로 받은 리스트를

pop을 통해 따로 빼준다. (위에 있는 JSON 데이터를 보시면 이해가 쉽다.)

 

그리고 tag 데이터가 빠진 review 데이터로 Review 테이블의 리뷰 객체를 만들고 해당 리뷰 객체를 항상 첫 번째 인자로 넣어주면서 리스트에 있는 모든 tag 데이터들을 테이블에 저장시켜준다.

 

POST Review의 Response

결과적으로 위처럼 의도한 대로 잘 생성된 것을 확인할 수 있다.

 

 

Reference:

https://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers

 

Serializer relations - Django REST framework

 

www.django-rest-framework.org