Django休息框架嵌套的序列化器与自引用对象

时间:2015-11-02 21:36:46

标签: django django-rest-framework

我已尝试在其他地方发布了一些针对此问题的解决方案,但没有运气。看起来DRF本身不支持它。有人有关于如何做到这一点的建议吗?

我有reports模型和section模型。部分定义如下:

class Section(models.Model):
    title = models.CharField(max_length=255)
    report = models.ForeignKey(Report)
    order = models.PositiveIntegerField()
    section = models.ForeignKey('self', related_name='section_section', blank=True, null=True)
    content = models.TextField(blank=True)

我希望在报告中显示数据:

[
    {
        "id": 1,
        "title": "test",
        "subtitle": "test",
        "section_set": [
            {
                "id": 1,
                "title": "test",
                "report": 1,
                "order": 1,
                "section_set": [
                    {
                        "id": 1,
                        "title": "test",
                        "report": 1,
                        "order": 1,
                        "section": null,
                        "content": "<p>test</p>"
                    },
                    {
                        "id": 2,
                        "title": "test",
                        "report": 1,
                        "order": 1,
                        "section": 2,
                        "content": "<p>test</p>"
                    },
                    {
                        "id": 3,
                        "title": "test",
                        "report": 1,
                        "order": 1,
                        "section": null,
                        "content": "<p>test</p>"
                    }
                ],
                "content": "<p>test</p>"
            },
            {
                "id": 2,
                "title": "test",
                "report": 1,
                "order": 1,
                "section": 2,
                "content": "<p>test</p>"
            },
            {
                "id": 3,
                "title": "test",
                "report": 1,
                "order": 1,
                "section": null,
                "content": "<p>test</p>"
            }
        ]
    }
]

我当前(尝试过)的实现如下所示:

class SubsectionSerializer(serializers.ModelSerializer):
class Meta:
    model = Section


class SectionSerializer(serializers.ModelSerializer):
    section = SubsectionSerializer()

    class Meta:
        model = Section
        fields = ('id', 'title', 'report', 'order', 'section', 'content')


class CountryReportSerializer(serializers.ModelSerializer):
    section_set = SectionSerializer(many=True)

    class Meta:
        model = CountryReport
        fields = ('id', 'title', 'subtitle', 'section_set')


class MapsSerializer(serializers.ModelSerializer):
    class Meta:
        model = Map
        fields = ('id', 'country', 'map_image', 'report')

但输出如下:

{
    "id": 1,
    "title": "test",
    "subtitle": "test",
    "section_set": [
        {
            "id": 1,
            "title": "Section 1",
            "report": 1,
            "order": 1,
            "section": null,
            "content": "<p>test</p>"
        },
        {
            "id": 2,
            "title": "Section 2",
            "report": 1,
            "order": 1,
            "section": null,
            "content": "<p>test</p>"
        },
        {
            "id": 3,
            "title": "Subsection 1",
            "report": 1,
            "order": 1,
            "section": {
                "id": 1,
                "title": "Section 1",
                "order": 1,
                "content": "<p>test</p>",
                "report": 1,
                "section": null
            },
            "content": "<p>test</p>"
        }
    ]
}

4 个答案:

答案 0 :(得分:2)

您定义子部分的方式并不会将其链接到您的部分字段,就像错误建议一样。您是否尝试过像这样定义序列化程序:

class SectionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Section

因为Section有一个FK to section,所以应该按照你对序列化程序的期望返回它。

要确保此序列化程序返回的JSON结果包含嵌套的JSON对象,而不是仅包含FK,您可以采用两种路径:

1),depth=

class SectionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Section
        depth=2

这将关闭FK,构建JSON对象,直到达到您指定的深度。

2)定义SubSerializer来处理JSON对象的创建:

class SubsectionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Section

class SectionSerializer(serializers.ModelSerializer):
    section = serializers.SubsectionSerializer()
    class Meta:
        model = Section
        fields = ('id', 'title', 'report', 'order', 'section', 'content')

------------------------ EDIT ---------------------- -----

为清楚起见,重命名模型的相关位可能是有意义的:

class Section(models.Model):
    title = models.CharField(max_length=255)
    report = models.ForeignKey(Report)
    order = models.PositiveIntegerField()
    parent_section = models.ForeignKey('self', related_name='child_sections', blank=True, null=True)
    content = models.TextField(blank=True)

使用新名称,您应该能够使用以下序列化程序:

class SectionSerializer(serializers.ModelSerializer):
    child_sections = serializers.SubsectionSerializer(many=True)
    class Meta:
        model = Section
        fields = ('id', 'title', 'report', 'order', 'child_sections', 'content')

答案 1 :(得分:2)

使用以下解决方案:

class RecursiveField(serializers.Serializer):
    def to_representation(self, value):
        serializer = self.parent.parent.__class__(value, context=self.context)
        return serializer.data


class SectionSerializer(serializers.ModelSerializer):
    children = RecursiveField(many=True)

    class Meta:
        model = Section
        fields = ('id', 'order', 'title', 'content', 'parent', 'children')


class CountryReportSerializer(serializers.ModelSerializer):
    section_set = serializers.SerializerMethodField('get_parent_sections')

    @staticmethod
    def get_parent_sections(self, obj):
        parent_sections = Section.objects.get(parent=None, pk=obj.pk)
        serializer = SectionSerializer(parent_sections)
        return serializer.data

    class Meta:
        model = CountryReport
        fields = ('id', 'title', 'subtitle', 'section_set')


class MapsSerializer(serializers.ModelSerializer):
    class Meta:
        model = Map
        fields = ('id', 'country', 'map_image', 'report')

答案 2 :(得分:1)

对于任何想要创建嵌套树状结构的人来说,这就是DRF 3.12.1对我有用的:

# Model class
class Foo:
    foo = models.ForeignKey('Foo', null=True, blank=True)

# Serializer class
class FooSerializer(serializers.ModelSerializer):
    foo = serializers.SerializerMethodField()

    def get_foo(self, obj):
        return FooSerializer(obj.foo).data if obj.foo else None

    class Meta:
        model = Foo
        fields = '__all__'

这将给出如下响应:

[
    {
        "id": 105,
        "a": {
            "id": 106,
            "a": null,
        }
    }
]

答案 3 :(得分:0)

KISS方法,RecursiveField序列化只返回值

\'

在model.py

class RecursiveField(serializers.ModelSerializer): 
    def to_representation(self, value):
        serializer_data = TypeSerializer(value, context=context).data
        return serializer_data
    class Meta:
            model = Type
            fields = '__all__'

class TypeSerializer(serializers.ModelSerializer):
    extends_type = RecursiveField(allow_null=True)

    class Meta:
        model = Type
        fields = '__all__'

你可以轻松地进入循环,为了避免这种情况,我们可以将TypeSerializer复制到SubTypeSerializer,并在上下文中使用on key counter计数或控制

class Type(models.Model):
    id = models.AutoField(primary_key=True)
    extends_type = models.ForeignKey('self', models.SET_NULL, null=True)