2013-11-14 84 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

謝謝,我不熟悉可瀏覽的文檔,所以我沒有。 –

相關問題