[Django Rest Framework] Serializer에 Model Instance를 인자값으로 보내기

2020. 8. 3. 18:27개발을 파헤치다/Django

반응형

DRF(Django REST Framework)를 사용할 때 Serializer는 정말 다양하게 쓰이는데요.
Serializer 내부의 create나 validate 메서드를 오버라이드 해서 각자 상황에 맞는 로직을 구성할 수 있습니다.

경우에 따라서는 Serializer 내부에서 다른 Serializer의 메서드를 호출할 수도 있는데요.
이번 포스팅에서는 이런 경우 한 Serializer에서 다른 Serializer에 어떻게 Model Instance를 인자 값으로 보낼 수 있는지 살펴보겠습니다.

먼저, 이런 경우가 언제 있는지 비즈니스 로직 흐름을 한번 설명드릴게요.
어떤 프로젝트를 생성한다고 할 때 프로젝트와 관련된 이미지들을 따로 저장하려고 합니다.
이 이미지들을 저장하는 테이블을 ImageTable이라고 가정해 보도록 하죠.
ImageTable에는 Project가 Foreign Key로

ProjectSerializer의 create 메서드에서는 프로젝트 생성에 필요한 정보들로 Project Model Instance를 생성하고
사용자가 보낸 이미지들을 ImageTable에 저장하려고 합니다.

이떄, 생성과정에서 오류가 생겨 Project만 생성되고 이미지 처리가 안 되는 일을 막기 위해 Transaction으로 처리할 예정입니다.

def create(self, validated_data):

        project_images = validated_data.pop("project_images")

        with transaction.atomic():
            # project instance를 생성한다
            celebrity_id = validated_data.get("celebrity")
            topic = validated_data.get("topic")
            donation_place_id = validated_data.get("donation_place")
            owner_id = validated_data.get("owner")

            instance_data = {
             
                "title": validated_data.get("title"),
                "story": validated_data.get("story"),
                "project_start_datetime": datetime.now(),
                "project_end_datetime": datetime.now(),
            }
            # Transacion으로 처리되기 때문에 아직 DB에는 적용되지 않았습니다
            project_instance = Project.objects.create(**instance_data)

            # 프로젝트 이미지를 ImageTable에 저장한다
            data = {
                "images": project_images, # InMemoryUpload된 이미지 파일들 List
            }
            image_serializer = ImageCreateSerializer(data=data)
            if image_serializer.is_valid(raise_exception=True):
                *image_serializer**.save(project=project_instance)*


        instance = None
        return instance

    class Meta:
        model = Project
        fields="__all__"

 

위의 코드를 살펴보면, Project의 Model Instance를 생성하고 이것을 ImageCreateSerializer에 인자 값으로 넣어주는 것을 확인할 수 있습니다.

한 Serializer에서 다른 Serializer로 원하는 값을 넘겨주고 싶을 때 이렇게 사용합니다.
Transaction 때문에 Project instance가 아직 생성되지 않았지만 저렇게 인자값으로 넘겨주면 ImageCreateSerializer에서 넘겨받아 프로젝트와 관련된 이미지를 생성할 때 사용할 수 있습니다.

 

"""
    이미지 테이블 처리 Serializer
"""

class ImageCreateSerializer(serializers.ModelSerializer):
    images = serializers.ListField(required=True)
    img_url = serializers.URLField(required=False)
    project = serializers.IntegerField(required=False)

    # 이미지를 S3에 업로드하고 URL을 받아서 저장한다
    def create(self, validated_data):
        images = validated_data.get("images")
        # save() 메서드에 인자값을 넣으면 create 메서드의 validated_data에
        # 인자값이 포함됩니다.
        *project_instance **= validated_data.get("project")
*
        for image in images:
            # 이미지를 S3에 업로드한다
            image_handler = S3Manager()
            img_url = image_handler.upload_file(image)

            # URL을 받아 Image Table에 저장한다
            data = {
                "img_url" : img_url,
                "project" : project_instance,
            }
            ImageTable.objects.create(**data)

        return project_instance


    class Meta:
        model = ImageTable
        fields = "__all__"

 

 

이렇게 ImageCreateSerializer에서는 아직 DB에 반영되지 않는 Project의 Model Instance를 인자값으로 받아
ImageTable에 프로젝트와 관련된 이미지들을 저장할 수 있습니다.
모든 로직이 끝나고 Transaction을 벗어나게 되면 그때에 DB에 적용(commit)이 완료됩니다.

반응형