Как включить родительский объект при сериализации модели Django? - PullRequest
0 голосов
/ 27 августа 2018

У меня есть две простые модели с отношением внешнего ключа, например:

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')

Итак, запрос GETдля Bar будет возвращаться что-то вроде этого:

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

Как мне изменить BarSerializer, чтобы вместо этого вернуть полный Foo объект, , как это:

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

?

Имейте в виду, мне все еще нужно иметь возможность создать Bar, предоставив только description и Foo ID.Я пробовал разные вещи, включая указание foo = FooSerializer() в BarSerializer.Проблема в том, что когда я хочу создать новый Bar и связать его с существующим Foo, как и раньше, он жалуется, что я не предоставил Foo свойство code.

Ответы [ 2 ]

0 голосов
/ 27 августа 2018

Решение :

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.

Пример :

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' }

0 голосов
/ 27 августа 2018

Простое и элегантное решение

переопределить to_represention() метод as,

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

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




Оригинальная версия

использование depth=1 в BarSerializer сериализатор


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


Ссылка
1. depth [DRF-Doc]

Обновление-1
Использование Два различных сериализатора для операций чтения и записи.


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

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


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()

    <b>def get_serializer_class(self):
        if self.action == 'create':
            return BarWriteSerializer
        return BarReadSerializer</b>

Полезная нагрузка, используемая при создании Bar,

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