序列化Django模型时如何包含父对象?

时间:2018-08-27 03:48:26

标签: django django-rest-framework

我有两个具有外键关系的简单模型,如下所示:

class Foo(models.Model):
    code = models.CharField(max_length=255)


class Bar(models.Model):
    foo = models.ForeignKey(Foo)
    description = models.CharField(max_length=255)

当前基于rest_framework的序列化器如下所示:

class FooSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Foo
        fields = ('id', 'code')


class BarSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Bar
        fields = ('id', 'foo', 'description')

因此,对Bar的GET请求将返回如下内容:

{
    "id": 1,
    "foo": 2,
    "description": "[…]"
}

如何更改BarSerializer以代替返回完整的Foo对象,,如下所示:

{
    "id": 1,
    "foo": {
        "id": 2,
        "code": "[…]"
    },
    "description": "[…]"
}

请记住,我仍然需要仅提供Bardescription ID来创建Foo。我尝试了各种操作,包括在foo = FooSerializer()中指定BarSerializer。问题是,当我想像以前一样创建新的Bar并将其链接到现有的 Foo时,它抱怨我没有提供Foocode属性。

2 个答案:

答案 0 :(得分:1)

简单而优雅的解决方案


覆盖 to_represention() 方法
class BarSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Bar
        fields = ('id', 'foo', 'description')

    def to_representation(self, instance):
        data = super().to_representation(instance)
        data['foo'] = FooSerializer(instance.foo).data
        return data




原始版本

depth=1 序列化程序中使用 BarSerializer


class BarSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Bar
        fields = ('id', 'foo', 'description')
        depth = 1


参考
1. depth [DRF-Doc]

Update-1
两个不同的序列化器 用于读取和写入操作。


class BarWriteSerializer(serializers.ModelSerializer):
    class Meta:
        model = Bar
        fields = ('id', 'foo', 'description')

    def to_representation(self, instance):
        data = super().to_representation(instance)
        data['foo'] = FooSerializer(instance.foo).data
        return data


class BarReadSerializer(serializers.ModelSerializer):
    class Meta:
        model = Bar
        fields = ('id', 'foo', 'description')
        depth = 1

,然后在您的视图中,将 get_serializer_class() 方法替换为

from rest_framework import viewsets


class SampleViewset(viewsets.ModelViewSet):
    queryset = Bar.objects.all()

    def get_serializer_class(self):
        if self.action == 'create':
            return BarWriteSerializer
        return BarReadSerializer

Bar创建时要使用的有效载荷,

{
    "foo":1,# The "pk" of "Foo" instance
    "description":"bar description"
}

答案 1 :(得分:0)

解决方案

class FooSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Foo
        fields = ('id', 'code')


class BarSerializer(serializers.ModelSerializer):
    foo = FooSerializer(models.Foo.objects.all(), read_only=True)
    foo_id = serializers.PrimaryKeyRelatedField(
        source='foo',
        queryset=models.Foo.objects.all(),
        write_only=True
    )

    class Meta:
        model = models.Bar
        fields = ('id', 'foo', 'foo_id', 'description')

使用嵌套的序列化程序作为read_only来获取完整的Foo对象。
使用write_only字段foo_id将其用于创建/更新。

现在,您的请求数据将如下所示:

{ 'foo_id': 1, 'description': 'foo bar' }

或者,如果您不需要两个字段,一个字段用于读取,另一个字段用于写入,则可以覆盖序列化程序的create / update方法来捕获foo的ID

示例

class BarSerializer(serializers.ModelSerializer):
    foo = FooSerializer(Foo.objects.all())

    class Meta:
        model = models.Bar
        fields = ('id', 'foo', 'description')

    def create(self, validated_data):
        foo = validated_data.pop('foo')
        bar = Bar.objects.create(
            foo=foo.id,
            **validated_data
        )
        return bar

    def update(self, instance, validated_data):
        foo = validated_data.pop('foo')
        instance.foo = foo
        instance.update(
            **validated_data
        )
        return instance

在这种情况下,请求数据将是:

{ 'foo': {'id': '1', 'code': 'AAA'}, 'description': 'foo bar' }