django-rest-framework 3.0 crea o actualiza en un serializador nested

Con django-rest-framework 3.0 y con estos modelos simples:

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) 

Y dada esta solicitud JSON:

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

¿Cómo puedo escribir un serializador nested para procesar este JSON y para cada page para el book dado crear una nueva página o actualizarla si existe?

 class RequestSerializer(serializers.Serializer): book_id = serializers.IntegerField() page = PageSerializer(many=True) class PageSerializer(serializers.ModelSerializer): class Meta: model = Page 

Sé que crear una instancia del serializador con una instance actualizará el actual, pero ¿cómo debo usarlo dentro del método de create del serializador nested?

En primer lugar, ¿desea admitir la creación de nuevas instancias de libros o solo actualizar las existentes?

Si solo quisieras crear nuevas instancias de libros, podrías hacer algo como esto …

 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 

Tenga en cuenta que no he incluido el book_id aquí. Cuando estemos creando instancias de libros, no incluiremos una ID de libro. Cuando estemos actualizando las instancias de libros, generalmente incluiremos la identificación del libro como parte de la URL, en lugar de en los datos de solicitud.

Si desea admitir tanto la creación como la actualización de instancias de libros, entonces debe pensar cómo desea manejar las páginas que no están incluidas en la solicitud, pero que actualmente están asociadas con la instancia de libros.

Puede elegir ignorar silenciosamente esas páginas y dejarlas como están, puede querer generar un error de validación o eliminarlas.

Supongamos que desea eliminar cualquier página que no esté incluida en la solicitud.

 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 

También es posible que desee que solo sea compatible con las actualizaciones de libros, y no con la creación, en cuyo caso, solo incluya el método update() .

También hay varias maneras en que podría reducir la cantidad de consultas, por ejemplo. utilizando la creación / eliminación masiva, pero lo anterior haría el trabajo de una manera bastante sencilla.

Como puede ver, hay sutilezas en los tipos de comportamiento que podría desear al tratar con datos nesteds, así que piense cuidadosamente qué comportamiento espera en varios casos.

También tenga en cuenta que he estado utilizando Serializer en el ejemplo anterior en lugar de ModelSerializer . En este caso, es más simple simplemente incluir todos los campos en la clase del serializador de manera explícita, en lugar de confiar en el conjunto automático de campos que ModelSerializer genera de forma predeterminada.

Simplemente puedes usar drf-writable-anested . Automáticamente haga que sus serializadores nesteds sean grabables y actualizables.

en ti 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 

¡Y eso es!

Además, la biblioteca admite el uso de solo una de las lógicas de create y update si no necesita ambas.