python - queryset - django-rest-framework 3.0 crear o actualizar en serializador anidado
django rest framework with django 2 (2)
Con django-rest-framework 3.0 y tener 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 anidado para procesar este JSON y para cada
page
del
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 instanciar el serializador con una
instance
actualizará el actual, pero ¿cómo debo usarlo dentro del método de
create
del serializador anidado?
En primer lugar, ¿desea apoyar la creación de nuevas instancias de libros o solo actualizar las existentes?
Si alguna vez 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 creamos instancias de libros, no incluiremos una identificación de libro.
Cuando estamos actualizando instancias de libros, normalmente incluiremos la identificación del libro como parte de la URL, en lugar de en los datos de la solicitud.
Si desea admitir tanto la creación como la actualización de instancias de libros, 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 libro.
Puede optar por ignorar silenciosamente esas páginas y dejarlas como están, es posible que desee generar un error de validación o que desee eliminarlas.
Supongamos que desea eliminar cualquier página no 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 admitir
solo
actualizaciones de libros y no admitir la creación, en cuyo caso,
solo
incluya el método
update()
.
También hay varias formas de reducir el número de consultas, por ejemplo. usando 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 puede desear cuando se trata de datos anidados, por lo tanto, piense detenidamente sobre exactamente qué comportamiento espera en varios casos.
También tenga en cuenta que he estado usando
Serializer
en el ejemplo anterior en lugar de
ModelSerializer
.
En este caso, es más simple incluir explícitamente todos los campos en la clase de serializador, en lugar de depender del conjunto automático de campos que
ModelSerializer
genera de forma predeterminada.
Simplemente puede usar drf-writable-nested . Automáticamente hace que sus serializadores anidados sean editables 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.