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 ответа:
во-первых, вы хотите поддержать создание новых экземпляров книг, или только обновление существующих?
если вы только когда-либо хотели создать новые экземпляры книги вы могли бы сделать что-то вроде этого...
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
логика, если вам не нужны обе.