2013-11-14 85 views
20

我想知道是否有人将Django REST框架与django-polymorphic相结合的Pythonic解决方案。django-rest-framework + django-polymorphic ModelSerialization

考虑:

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的休息框架所有GalleryItems只会给我GalleryItem(父模型)的字段列表,因此:ID,gallery_item_field和polymorphic_ctype 。这不是我想要的。如果是Photo实例,我需要custom_photo_field,如果是Video,我需要custom_image_field。

回答

24

到目前为止,我只测试了本作的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你可能想要做的事请求similiar作为与to_internal_value高清重写to_representation定义。

+1

我找不到了'to_native'方法的任何文件。它什么时候被叫? –

+0

http://www.django-rest-framework.org/topics/3.0-announcement/#changes-to-the-custom-field-api –

+4

我猜测POST和PUT可能会稍微困难一些(尽管仍然可行),因为您需要确定用户想要提交的内容,在验证之前(如果某个字段丢失,可能无法确定)。 恕我直言,它使用单独的端点写入请求更清洁。 – WhyNotHugo

1

为了完成,我添加了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) 
4

这是一个通用且可重用的解决方案。它适用于通用的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, 
     } 
+1

伟大的解决方案!您是否可以将此与DRF的可浏览文档相集成? –

+0

谢谢,我不熟悉可浏览的文档,所以我没有。 –

相关问题