Django 에서 PolymorphicSerializer 와 WritableNestedModelSerializer 를 같이 사용하기

Ashe·2020년 2월 14일
0

개요

PolymorphicSerializer 를 사용하고 있을 때 프론트에 주고 받는 데이터의 형태를 같게하기 위해 WritableNestedModelSerializer 를 사용할 때가 왔다.

PolymorphicSerializer, WritableNestedModelSerializer 를 사용할 때 _save_kwargs KeyError 가 발생했다. WritableNestedModelSerializer 의 save가 호출되지 않았음을 알았으나 왜 호출되지 않았는지 몰라 하나하나 타고 들어갔다.

문제

Models

from django.db import models
from polymorphic.models import PolymorphicModel

class Component(PolymorphicModel):
    class Meta:
        verbose_name = "Component"


class ComponentA(Component):
    class Meta:
        verbose_name = "ComponentA"

    subComponent = models.OneToOneField(
        "ComponentAA"
        on_delete=models.CASCADE
    )

class ComponentAA(models.Model):
    class Meta:
        verbose_name = "Corpus media"

    someText = models.Charfield(
        default = "some string"
    )

Serializers

from rest_framework import serializers, status, response
from drf_writable_nested import WritableNestedModelSerializer
from rest_polymorphic.serializers import PolymorphicSerializer
from .models import Component, ComponentA, ComponentAA

class ComponentAASerializer(serializer.ModelSerializer):
    class Meta:
        model = ComponentAA
        fields = ('id', 'someText')


class ComponentASerializer(WritableNestedModelSerializer):
    subComponent = ComponentAASerializer(many=False)

    class Meta:
        model = ComponentA
        fields = ('id', 'subComponent')


class ComponentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Component
        fields = '__all__'


class ComponentPolymorphicSerializer(PolymorphicSerializer):
	class Meta:
    	model = Component
        fields = '__all__'

    model_serializer_mapping = {
        Component: ComponentSerializer,
        ComponentA: ComponentASerializer
    }

    def to_resource_type(self, model_or_instance):
        return model_or_instance._meta.object_name.lower()

APIView

class ComponentCreateAPIView(generics.ListCreateAPIView):
    queryset = Component.objects.all()
    serializer_class = ComponentPolymorphicSerializer

기존에는 위처럼 사용했다. ComponentPolymorphicSerializerPolymorphicSerializer만 상속받아 resourceType에 따라 Serializer 를 나누어 전달하는 목적이고 나머지는 하위 항목들은 WritableNestedModelSerializer 을 상속받아 하위 모델까지 저장되도록 하고싶었다.

save_kwargs 는 WritableNestedModelSerializersave 에서 채워주게 된다.
하지만 PolymorphicSerializer 와 함께 사용하게되면 WritableNestedModelSerializersave 가 호출되지 않아 ComponentASerializer 를 저장하려 할 때 _save_kwargs KeyError 가 발생한다.

해결책

ComponentCreateAPIViewperform_save 에서는 serializer_class 의 save 메서드를 호출한다. 그럼ComponentPolymorphicSerializer 의 save 메서드가 호출된다. Serailizer 기본 save 가 호출 된 후 바로 하위 Serializer(ComponentASerializer, ComponentAASerializer)의 create 메서드가 실행된다.
하위 Serializer(ComponentASerializer, ComponentAASerializer) 들은 _save_kwargs 를 채워넣지 않은 상태이기 때문에 _get_save_kwrags 메서드를 실행할 때 _save_kwargs 를 사용하려 하면 Error 가 발생한다.

Polymorphic 과 같이 사용하는 Serializer 를 재작성해주었다.

class WritableNestedPolymorphicModelSerializer(WritableNestedModelSerializer):
	def _get_save_kwargs(self, field_name):
        if hasattr(self, '_save_kwargs'):
            save_kwargs = self._save_kwargs.get(field_name, {})
            if not isinstance(save_kwargs, dict):
                raise TypeError(
                    _("Arguments to nested serializer's `save` must be dict's")
                )
        else:
            save_kwargs = {}
        return save_kwargs
profile
Qué será, será

0개의 댓글