django-rest-framework 3.0 создание или обновление во вложенном сериализаторе


С django-rest-framework 3.0 и имея эти простые модели:

class Book(models.Model):
    title = models.CharField(max_length=50)


class Page(models.Model):
    book = models.ForeignKey(Books, related_name='related_book')
    text = models.CharField(max_length=500)

и учитывая этот запрос JSON:

{
   "book_id":1,
   "pages":[
      {
         "page_id":2,
         "text":"loremipsum"
      },
      {
         "page_id":4,
         "text":"loremipsum"
      }
   ]
}

как я могу написать вложенный сериализатор для обработки этого JSON и для каждого page для данного book либо создать новую страницу, либо обновить, если она существует.

class RequestSerializer(serializers.Serializer):
    book_id = serializers.IntegerField()
    page = PageSerializer(many=True)


class PageSerializer(serializers.ModelSerializer):
    class Meta:
        model = Page

Я знаю, что создание экземпляра сериализатора с instance будет обновлять текущий, но как я должен использовать его внутри create метод вложенный сериализатор?

2 64

2 ответа:

во-первых, вы хотите поддержать создание новых экземпляров книг, или только обновление существующих?

если вы только когда-либо хотели создать новые экземпляры книги вы могли бы сделать что-то вроде этого...

class PageSerializer(serializers.Serializer):
    text = serializers.CharField(max_length=500)

class BookSerializer(serializers.Serializer):
    page = PageSerializer(many=True)
    title = serializers.CharField(max_length=50)

    def create(self, validated_data):
        # Create the book instance
        book = Book.objects.create(title=validated_data['title'])

        # Create or update each page instance
        for item in validated_data['pages']:
            page = Page(id=item['page_id'], text=item['text'], book=book)
            page.save()

        return book

обратите внимание, что я не включил book_id здесь. Когда мы создаем экземпляры книг, мы не будем включать идентификатор книги. Когда мы обновляем экземпляры книг, мы обычно включаем идентификатор книги как часть URL-адреса, а не в запрос данные.

если вы хотите поддерживать как создание, так и обновление экземпляров книг, то вам нужно подумать о том, как вы хотите обрабатывать страницы, которые не включены в запрос, но are в настоящее время связан с экземпляром книги.

вы можете молча игнорировать эти страницы и оставить их такими, какие они есть, вы можете вызвать ошибку проверки или удалить их.

предположим, что вы хотите удалить любые страницы не входит в заявку.

def create(self, validated_data):
    # As before.
    ...

def update(self, instance, validated_data):
    # Update the book instance
    instance.title = validated_data['title']
    instance.save()

    # Delete any pages not included in the request
    page_ids = [item['page_id'] for item in validated_data['pages']]
    for page in instance.books:
        if page.id not in page_ids:
            page.delete()

    # Create or update page instances that are in the request
    for item in validated_data['pages']:
        page = Page(id=item['page_id'], text=item['text'], book=instance)
        page.save()

    return instance

также возможно, что вы захотите только поддержка обновления книг, а не создание поддержки, в этом случае,только включить update() метод.

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

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

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

вы можете просто использовать drf-writable-nested. Это автоматически делает ваши вложенные сериализаторы доступными для записи и обновления.

вы serializers.py:

from drf_writable_nested import WritableNestedModelSerializer

class RequestSerializer(WritableNestedModelSerializer):
    book_id = serializers.IntegerField()
    page = PageSerializer(many=True)


class PageSerializer(serializers.ModelSerializer):
    class Meta:
        model = Page

и это все!

также библиотека поддерживает использование только одного элемента create и update логика, если вам не нужны обе.