简体   繁体   中英

DRF: Serializer for heterogneous list of related models

Roughly said, I have the following schema in ORM:

class Page(models.Model):

    title = models.CharField(max_length=255, null=False, blank=False)

    @property
    def content(self):
        return [Video.objects.all()[0], Text.objects.all()[0], Video.objects.all()[1]]

and I have the following set of classes to support serialization for detailed view:

class ContentSerializer(serializers.ListSerializer):

    class Meta:
        model = ???
        fields = '???'


class PageDetailSerializer(serializers.ModelSerializer):
    content = ContentSerializer(many=True)

    class Meta:
        model = Page
        fields = ('title', 'content', )

So I'm looking for a way to serialize that Page.content property - which is:

  1. a list;
  2. will contain heterogeneous data (combination of, let's say Video , Audio , Text and other models.

So I need somehow patch one of builtin serializers to iterate thru the list and check type of each object. And then decide how to serialize each one. Eg I could prepare kind of dynamically created ModelSerializer with:

obj_type = type(obj)

class ContentModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = obj_type
        fields = '__all__'

serialized_obj = ContentModelSerializer(obj)

How could I implement that?

You can simply achieve this by overriding the to_representation method of Page serializer. like this:

class PageDetailSerializer(serializers.ModelSerializer):

class Meta:
    model = Page
    fields = ('title', 'content', )

def to_representation(self, instance):
    ctx = super(PageDetailSerializer, self).to_representation(instance)
    content = instance.content         # property field of page, will return list of items
    serialized_content = []
    for c in content:
        if type(c) == Video:
            serialized_content.append({... serialized data of video type ..})
        elif type(c) == ...
            # other conditions here..

I had googled a lot before found the solution. This article has a reference to SerializerMethodField , which let you add custom handler for a field. And the final solution, which worked for me is:

class PageDetailSerializer(serializers.ModelSerializer):
    _cache_serializers = {}

    content = serializers.SerializerMethodField()

    class Meta:
        model = Page
        fields = ('title', 'content', )

    def _get_content_item_serializer(self, content_item_type):
        if content_item_type not in self._cache_serializers:
            class ContentItemSerializer(serializers.ModelSerializer):
                class Meta:
                    model = content_item_type
                    exclude = ('id', 'page', )
            self._cache_serializers[content_item_type] = ContentItemSerializer
        return self._cache_serializers[content_item_type]

    def get_content(self, page):
        return [
            self._get_content_item_serializer(type(content_item))(content_item).data for content_item in page.content
        ]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM