2013-11-14 21 views
20

誰かがDjango RESTフレームワークをDjango-polymorphicと組み合わせるPythonのソリューションを持っているのだろうかと思いました。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() 

私はそれは私だけGalleryItem(親モデル)のフィールドを与えるだろうジャンゴレストフレームワーク内のすべてのGalleryItemsのリストをしたい場合は、それ故に:ID、gallery_item_field、およびpolymorphic_ctype 。それは私が望むものではありません。写真のインスタンスの場合はcustom_photo_field、動画の場合はcustom_image_fieldが必要です。

答えて

24

は、これまでのところ、私は唯一のGETリクエストのためにこれをテストしたが、これは動作します:POSTの場合

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) 

、あなたが何かをしたいかもしれない要求をPUT似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が少し難しいかもしれないと推測しています)あなたが提出することを意図したものを確認する必要があるので、検証の前に(フィールドがない場合、特定するのが不可能かもしれません)。 IMHOでは、書き込み要求に別々のエンドポイントを使用する方がクリーンです。 – 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

ありがとう、私は閲覧可能なドキュメントに精通していないので、私はそうしなかった。 –

関連する問題