Mic*_*ter 22 python django django-rest-framework
我想知道是否有人有一个将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()
Run Code Online (Sandbox Code Playgroud)
如果我想要一个django-rest-framework中所有GalleryItem的列表,它只会给我GalleryItem(父模型)的字段,因此:id,gallery_item_field和polymorphic_ctype.那不是我想要的.我想要custom_photo_field,如果它是一个Photo实例,我想要custom_image_field,如果它是一个视频.
Mic*_*ter 26
到目前为止,我只测试了这个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)
Run Code Online (Sandbox Code Playgroud)
对于POST和PUT请求,您可能希望执行类似于使用to_internal_value def覆盖to_representation定义的操作.
为了完善起见,我添加了to_internal_value()实现,因为我在最近的项目中需要它。
如何确定类型
方便地区分不同的“阶级”;为此,我已将type属性添加到基本多态模型中:
class GalleryItem(PolymorphicModel):
gallery_item_field = models.CharField()
@property
def type(self):
return self.__class__.__name__
Run Code Online (Sandbox Code Playgroud)
这允许将typeas称为“字段”和“只读字段”。
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)
Run Code Online (Sandbox Code Playgroud)
这是一个通用且可重用的解决方案。它是针对通用的,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)
Run Code Online (Sandbox Code Playgroud)
并使用它:
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,
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5535 次 |
| 最近记录: |