Django DRF serializer Пользовательские реляционные поля Как построить возвращаемое значение to_internal_value? - PullRequest
0 голосов
/ 28 января 2020

Я пытаюсь реализовать что-то очень похожее на учебник по Djang Rest Framework Пользовательские реляционные поля .

Для напоминания приведен фрагмент кода:

import time

class TrackListingField(serializers.RelatedField):
    def to_representation(self, value):
        duration = time.strftime('%M:%S', time.gmtime(value.duration))
        return 'Track %d: %s (%s)' % (value.order, value.name, duration)

class AlbumSerializer(serializers.ModelSerializer):
    tracks = TrackListingField(many=True)

    class Meta:
        model = Album
        fields = ['album_name', 'artist', 'tracks']

И «Это настраиваемое поле будет затем сериализовано в следующее представление» (цитируется в руководстве):

{
    'album_name': 'Sometimes I Wish We Were an Eagle',
    'artist': 'Bill Callahan',
    'tracks': [
        'Track 1: Jim Cain (04:39)',
        'Track 2: Eid Ma Clack Shaw (04:19)',
        'Track 3: The Wind and the Dove (04:34)',
        ...
    ]
}

Я понимаю это и реализовал его для моего конкретного случая.

Что Я не понимаю, это способ реализации to_internal_value(self, data), поскольку я хочу предоставить API для чтения и записи.

Я понимаю, что to_internal_value(self, data) должен возвращать объект AlbumTrack, но я не понимаю, как его создать. В частности, как вернуть идентификатор, связанный с альбомом?

Если мы опубликуем вышеприведенную структуру JSON, to_internal_value(self, data) будет вызываться один раз для каждой дорожки с 'Track 1: Jim Cain (04:39)' ... для data ценности. Я не вижу, как мы можем обновить модель треков из этих data значений.

1 Ответ

1 голос
/ 29 января 2020

Кажется, вы пытаетесь реализовать записываемые вложенные сериализаторы. Хотя вложенные сериализаторы по умолчанию доступны только для чтения, в DRF есть раздел, в котором объясняется, как реализовать записываемые: https://www.django-rest-framework.org/api-guide/relations/#writable -nested-сериализаторы

Поскольку вы хотите, чтобы TrackListingField сериализовать модель Track, которую она должна наследовать от ModelSerializer:

class TrackSerializer(serializers.ModelSerializer):
    class Meta:
        model = Track
        fields = ['order', 'name', 'duration']

Затем вам придется переопределить метод create для AlbumSerializer:

    def create(self, validated_data):
        tracks_data = validated_data.pop('tracks')
        album = Album.objects.create(**validated_data)
        for track_data in tracks_data:
            Track.objects.create(album=album, **track_data)
        return album

Пожалуйста обратите внимание, что выше будет делать один запрос к базе данных на трек. Вы можете использовать Track.objects.bulk_create, чтобы сделать только один запрос для создания всех треков.

Чтобы ответить на ваш первоначальный вопрос о to_internal_value, вы можете увидеть, что по умолчанию, добавив этот оператор print в переопределено to_internal_value:

class TrackSerializer(serializers.ModelSerializer):
    ...
    def to_internal_value(self, data):
        default_return_value = super(TrackSerializer, self).to_internal_value(data)
        print(default_return_value)
        return default_return_value

В случае ModelSerializer DRF использует вывод OrderedDict для to_internal_value. Ваш пользовательский to_internal_value должен извлечь order, name и duration из строки data, используя регулярное выражение, и поместить их в OrderedDict. Однако в этом случае, вероятно, было бы проще использовать словарь для представления треков.

...