DRF:用于相关模型异构列表的序列化器

时间:2019-02-20 18:30:22

标签: python django serialization orm django-rest-framework

粗略地说,我在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]]

并且我有以下几组类可支持序列化以获得详细视图:

class ContentSerializer(serializers.ListSerializer):

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


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

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

所以我正在寻找一种序列化Page.content属性的方法-即:

  1. 列表;
  2. 将包含异构数据(例如VideoAudioText和其他模型的组合。

因此,我需要以某种方式修补内置的序列化程序之一,以遍历列表并检查每个对象的类型。然后决定如何序列化每个。例如。我可以准备以下一种动态创建的ModelSerializer

obj_type = type(obj)

class ContentModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = obj_type
        fields = '__all__'

serialized_obj = ContentModelSerializer(obj)

我该如何实现?

2 个答案:

答案 0 :(得分:1)

您可以通过覆盖to_representation序列化器的Page方法来简单地实现此目的。像这样:

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..

答案 1 :(得分:0)

在找到解决方案之前,我已经在Google上搜索了很多。 This article引用了SerializerMethodField,可让您为字段添加自定义处理程序。对我有用的最终解决方案是:

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
        ]