2013-11-14 4 views
20

누군가 장고 - REST 프레임 워크를 장고 - 다형성과 결합하는 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() 

나는 단지 나에게 GalleryItem (상위 모델)의 필드를 줄 것이다 장고 - 나머지 프레임 워크의 모든 GalleryItems의 목록을 원하는 경우, 따라서 : 아이디, 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에는 파이썬 클래스 이름이 포함됩니다.

당신은 "필드"로 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

감사합니다. 브라우즈 기능이있는 문서에 익숙하지 않아서 그렇지 않았습니다. –

관련 문제