DRF: Полный пример ListSerializer как Admin-Inline в Django?

Я пытаюсь разобраться со списком DRF ListSerializer в сочетании с foreignkey-моделью. Цель - получить что-то похожее на Django's admin-inline Formula. К сожалению, я не могу найти полный пример в документации или SO примеры: две модели, два сериализатора и viewset.

Предположим, что у нас есть модель, немного похожая на модель в примере SO DRF ListSerializer и ListField :

class Musician(models.Model):

    name = models.CharField(max_length=50)
    comment = models.TextField(blank=True)

class Album(models.Model):
    artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)

Поэтому я пытаюсь поместить их в сериализаторы следующим образом:

class AlbumSerializer(serializers.ModelSerializer):

    class Meta:
        fields = 'artist', 'title'
        model = Album

class MusicianSerializer(serializers.ModelSerializer):

    class Meta:
        fields = 'name', 'comment', 'albums'
        model = Musician

    albums = serializers.ListSerializer(child=AlbumSerializer(), source='album_set')

Затем я хочу построить на нем ModelViewSet, чтобы я мог работать с ним, как в Django-Admin-Inline. Но это не работает просто так:

class MusicianViewSet(viewsets.ModelViewSet):

    queryset = Musician.objects.all()
    serializer_class = MusicianSerializer

Но это трудно проверить, HTML front-end действительно работает, и в тестах я получил (в немного другой ситуации) сообщение об ошибке, что-то вроде:

{'album_set': [ErrorDetail(string='This field is required.', code='required')]}

чего я не смог понять, даже после многочасового копания в исходном коде. Так чего же мне не хватает? А в идеале, каким должен быть полноценный рабочий пример с POST, PUT и DESTROY, создающий модификацию или удаление на дочерних объектах модели?

[Edit: I'm using Django==4.0.1 and django-extensions==3.1.5]

Вы можете использовать related_name в своей модели

class Album(models.Model):
    artist = models.ForeignKey(Musician, on_delete=models.CASCADE, related_name='albums')
    name = models.CharField(max_length=100)

затем вызовите его в вашем сериализаторе

class MusicianSerializer(serializers.ModelSerializer):
    albums = AlbumSerializer(many=True, read_only=True)
    class Meta:
        fields = ['name', 'comment', 'albums']
        model = Musician

Ок, итак, были разные ошибки, и вот некоторые подсказки, чтобы разделить проблему. Сначала было две ошибки в сериализаторах, мы не должны сериализовать родителя artist, и мы должны добавить метод create в сериализатор Musician:

class AlbumSerializer(serializers.ModelSerializer):

    class Meta:
        fields = 'title',
        model = Album


class MusicianSerializer(serializers.ModelSerializer):

    class Meta:
        fields = 'id', 'name', 'comment', 'albums',
        model = Musician

    albums = AlbumSerializer(many=True)

    def create(self, validated_data):
        album_data = validated_data.pop('albums')
        muse = Musician.objects.create(**validated_data)
        for alb in album_data:
            muse.albums.create(**alb)
        return muse

Это можно проверить непосредственно в тестах, вот несколько подсказок как для сериализации отношений, так и косвенно для написания тестов о сериализаторе: https://www.django-rest-framework.org/api-guide/relations/ .

С его помощью я могу запустить скрипт django через команду runscript django-extension, как с rest_framework.test.RequestsClient, так и с requests.Session (в последнем случае мы запустим сервер командой runserver).

Сценарий может выглядеть следующим образом:

def run():
    client = requests.Session()
    client.auth = requests.auth.HTTPBasicAuth(username, password)

   TEST_DATA = {
       "name": "Miles Davis",
       "comment": "Great Stuff",
       "albums": [{"title": "B.B."}, {"title": "Don't know"}],
    }
    response = client.post(create_music_url, json=TEST_DATA)

Я нашел это полезным, чтобы понять, что код правильный и что тест был неправильным, который я все еще не могу заставить работать...

[Edit] Вот сообщения об ошибках в тесте с использованием rest_framework.test.APITestCase и его self.client и force_authenticate :

   response = self.client.post(music_url, json=TEST_DATA)

response.data является следующим: {'name': [ErrorDetail(string='This field is required.', code='required')], 'albums': [ErrorDetail(string='This field is required.', code='required')]}

и с

   response = self.client.post(music_url, TEST_DATA)

Я получаю {'albums': [ErrorDetail(string='This field is required.', code='required')]}

Имхо, эти сообщения об ошибках, мягко говоря, сбивают с толку...

Вернуться на верх