【问题标题】:django-rest-framework + django-polymorphic ModelSerializationdjango-rest-framework + django-多态模型序列化
【发布时间】:2013-11-27 09:03:58
【问题描述】:

我想知道是否有人有将 Django REST 框架与 django-polymorphic 相结合的 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。

【问题讨论】:

    标签: python django django-rest-framework


    【解决方案1】:

    这是一个通用且可重复使用的解决方案。这是一个通用的Serializer,但修改它以使用ModelSerializer 并不难。它也不处理父类的序列化(在我的例子中,我更多地将父类用作接口)。

    from typing import Dict, Type
    
    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, Type[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, Type[serializers.Serializer]]:
            """
            Return serializer map
            """
            return {
                ChildClass1.__name__: ChildClass1Serializer,
                ChildClass2.__name__: ChildClass2Serializer,
            }
    

    【讨论】:

    • 很好的解决方案!您能否将其与 DRF 的可浏览文档集成?
    • 谢谢,我不熟悉可浏览的文档,所以我没有。
    • 感谢您的解决方案。快速说明:您的序列化程序映射的类型应该是 Dict[str, Type[serializers.Serializer]] 而不是 Dict[str, serializers.Serializer]
    【解决方案2】:

    为了完成,我添加了to_internal_value() 实现,因为我在最近的项目中需要它。

    如何判断类型

    有可能区分不同的“类”很方便;为此,我将 type 属性添加到基本多态模型中:

    class GalleryItem(PolymorphicModel):
        gallery_item_field = models.CharField()
    
        @property
        def type(self):
            return self.__class__.__name__
    

    这允许将type 称为“字段”和“只读字段”。

    type 将包含 python 类名。

    向序列化器添加类型

    您可以将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)
    

    【讨论】:

      【解决方案3】:

      到目前为止,我只针对 GET 请求进行了测试,并且可行:

      class PhotoSerializer(serializers.ModelSerializer):
      
          class Meta:
              model = models.Photo
      
      
      class VideoSerializer(serializers.ModelSerializer):
      
          class Meta:
              model = models.Video
      
      
      class GalleryItemModuleSerializer(serializers.ModelSerializer):
      
          class Meta:
              model = models.GalleryItem
      
          def to_representation(self, obj):
              """
              Because GalleryItem is Polymorphic
              """
              if isinstance(obj, models.Photo):
                  return PhotoSerializer(obj, context=self.context).to_representation(obj)
              elif isinstance(obj, models.Video):
                 return VideoSerializer(obj, context=self.context).to_representation(obj)
              return super(GalleryItemModuleSerializer, self).to_representation(obj)
      

      对于 POST 和 PUT 请求,您可能希望执行类似于使用 to_internal_value 定义覆盖 to_representation 定义的操作。

      【讨论】:

      • 我找不到to_native 方法的任何文档。什么时候调用?
      • 我猜 POST 和 PUT 可能会稍微困难一些(尽管仍然可行),因为您需要在验证之前确定用户打算提交的内容(如果缺少字段,则可能是不可能的待确认)。恕我直言,为写入请求使用单独的端点更清洁。
      • to_native 更改为to_representation
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-04-13
      • 2016-11-14
      • 2014-11-12
      • 1970-01-01
      • 2020-11-18
      • 2013-11-11
      • 2018-06-07
      相关资源
      最近更新 更多