python - viewset - django-rest-framework+django-polymorphic ModelSerialization




rest framework choicefield (2)

누구든지 Django REST 프레임 워크를 장고 다형성과 결합하는 Pythonic 솔루션을 가지고 있는지 궁금합니다.

주어진:

class GalleryItem(PolymorphicModel):
    gallery_item_field = models.CharField()

class Photo(GalleryItem):
    custom_photo_field = models.CharField()

class Video(GalleryItem):
    custom_image_field = models.CharField()

django-rest-framework에서 모든 GalleryItem의 목록을 원한다면 GalleryItem (상위 모델)의 필드 만 제공하게됩니다. 따라서 id, gallery_item_field 및 polymorphic_ctype. 그건 내가 원하는 것이 아니야. 사진 인 경우 custom_photo_field를, 동영상 인 경우 custom_image_field를 원합니다.


다음은 일반적인 재사용 가능한 솔루션입니다. 일반적인 Serializer 이지만 ModelSerializer 를 사용하도록 수정하는 것은 어렵지 않습니다. 또한 부모 클래스를 직렬화하는 것을 처리하지 않습니다 (필자는 부모 클래스를 인터페이스로 더 사용합니다).

from typing import Dict

from rest_framework import serializers


class PolymorphicSerializer(serializers.Serializer):
    """
    Serializer to handle multiple subclasses of another class

    - For serialized dict representations, a 'type' key with the class name as
      the value is expected: ex. {'type': 'Decimal', ... }
    - This type information is used in tandem with get_serializer_map(...) to
      manage serializers for multiple subclasses
    """
    def get_serializer_map(self) -> Dict[str, serializers.Serializer]:
        """
        Return a dict to map class names to their respective serializer classes

        To be implemented by all PolymorphicSerializer subclasses
        """
        raise NotImplementedError

    def to_representation(self, obj):
        """
        Translate object to internal data representation

        Override to allow polymorphism
        """
        type_str = obj.__class__.__name__

        try:
            serializer = self.get_serializer_map()[type_str]
        except KeyError:
            raise ValueError(
                'Serializer for "{}" does not exist'.format(type_str),
            )

        data = serializer(obj, context=self.context).to_representation(obj)
        data['type'] = type_str
        return data

    def to_internal_value(self, data):
        """
        Validate data and initialize primitive types

        Override to allow polymorphism
        """
        try:
            type_str = data['type']
        except KeyError:
            raise serializers.ValidationError({
                'type': 'This field is required',
            })

        try:
            serializer = self.get_serializer_map()[type_str]
        except KeyError:
            raise serializers.ValidationError({
                'type': 'Serializer for "{}" does not exist'.format(type_str),
            })

        validated_data = serializer(context=self.context) \
            .to_internal_value(data)
        validated_data['type'] = type_str
        return validated_data

    def create(self, validated_data):
        """
        Translate validated data representation to object

        Override to allow polymorphism
        """
        serializer = self.get_serializer_map()[validated_data['type']]
        return serializer(context=self.context).create(validated_data)

그리고 그것을 사용하려면 :

class ParentClassSerializer(PolymorphicSerializer):
    """
    Serializer for ParentClass objects
    """
    def get_serializer_map(self) -> Dict[str, serializers.Serializer]:
        """
        Return serializer map
        """
        return {
            ChildClass1.__name__: ChildClass1Serializer,
            ChildClass2.__name__: ChildClass2Serializer,
        }

완료를 위해서, 최근 프로젝트에서 이것을 필요로하기 때문에 to_internal_value() 구현을 추가하고 있습니다.

유형을 결정하는 방법

다른 "클래스"를 구별 할 수있는 편리한 기능. 그래서이 목적을 위해 기본 polymorphic 모델에 type 속성을 추가했습니다.

class GalleryItem(PolymorphicModel):
    gallery_item_field = models.CharField()

    @property
    def type(self):
        return self.__class__.__name__

type 을 "필드"및 "읽기 전용 필드"로 호출 할 수 있습니다.

type 은 python 클래스 이름을 포함합니다.

Serializer에 유형 추가

"필드"와 "읽기 전용 필드"에 type 을 추가 할 수 있습니다 (모든 하위 모델에서 사용하려는 경우 모든 직렬 변환기에서 유형 필드를 지정해야 함)

class PhotoSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Photo

    fields = ( ..., 'type', )
    read_only_fields = ( ..., 'type', )


class VideoSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Video

    fields = ( ..., 'type', )
    read_only_fields = ( ..., 'type', )

class GalleryItemModuleSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.GalleryItem

    fields = ( ..., 'type', )
    read_only_fields = ( ..., 'type', )

    def to_representation(self, obj):
        pass # see the other comment

    def to_internal_value(self, data):
    """
    Because GalleryItem is Polymorphic
    """
    if data.get('type') == "Photo":
        self.Meta.model = models.Photo
        return PhotoSerializer(context=self.context).to_internal_value(data)
    elif data.get('type') == "Video":
        self.Meta.model = models.Video
        return VideoSerializer(context=self.context).to_internal_value(data)

    self.Meta.model = models.GalleryItem
    return super(GalleryItemModuleSerializer, self).to_internal_value(data)






django-rest-framework