Cuatro líneas que generan once campos, y el listado que pasa de 2 consultas a 399 por enseñar el nombre de la editorial.
⏱ 17 min de lectura
ModelSerializer escribe el serializador por ti a partir del modelo. Ahorra mucho código y es lo que se usa el 90 % de las veces.
Y las relaciones son la parte difícil de una API: decidir si la editorial de un libro sale como número o como objeto no es cosmético, es la diferencia entre dos consultas y trescientas noventa y nueve.
Imprimir el serializador es la herramienta de depuración más útil de DRF y casi nadie la conoce. Enseña exactamente qué campos hay, con qué tipo y con qué reglas, que es la respuesta a la mitad de las preguntas de "por qué esto no valida".
Cuatro cosas que ha deducido solo:
Las etiquetas salen del verbose_name.Título, Páginas, ISBN, con sus tildes. Un modelo escrito en español da una API documentada en español gratis.
El unique=True del modelo se convierte en un validador, así que un ISBN repetido da un 400 limpio en vez de un error de base de datos.
Los campos con auto_now son de solo lectura. Bien deducido.
Las relaciones salen como identificadores, y eso es lo que vamos a cambiar.
Cuando quien la usa necesita los datos y no otra petición
Por campo es la más infravalorada. Si tus editoriales tienen un código único, SlugRelatedField(slug_field='codigo') da una API en la que "editorial": "anagrama" se lee y se escribe, sin que nadie tenga que saber que la editorial es el número 2.
Anidado es lo que casi siempre quiere quien consume la API, y es lo que cuesta.
Lo que sorprende es la segunda línea: devolver solo los identificadores de los autores ya cuesta una consulta por libro. La intuición dice que un número es gratis, y no lo es: es una relación de muchos a muchos, y para saber qué identificadores tiene cada libro hay que ir a la tabla intermedia.
Y de ahí la conclusión práctica: la optimización no depende de la forma que elijas, depende de que optimices. Anidado y optimizado cuesta lo mismo que identificadores y optimizado: dos consultas.
El arreglo va en la vista, no en el serializador:
python
classLibroViewSet(viewsets.ModelViewSet):
queryset =(Libro.objects
.select_related('editorial')# clave ajena: una unión.prefetch_related('autores'))# muchos a muchos: otra consulta
serializer_class = LibroSerializer
Es exactamente lo de la lección 15 del módulo 4, y aquí duele más porque en una web solo se ven veinte filas de golpe y en una API es normal pedir cien.
is_valid(): True
AssertionError: The `.create()` method does not support writable nested fields
by default. Write an explicit `.create()` method for serializer `LibroSerializer`,
or set `read_only=True` on nested serializer fields.
Fíjate en que valida bien y revienta al guardar. DRF no sabe qué quieres: ¿crear una editorial nueva? ¿buscar la que se parezca? ¿actualizar la que hay? Son decisiones tuyas, y hace bien en no adivinar. El mensaje, además, dice las dos salidas.
La solución habitual, y la que conviene por defecto, es dos campos: uno para leer y otro para escribir.
python
classLibroSerializer(serializers.ModelSerializer):
editorial = EditorialSerializer(read_only=True)
editorial_id = serializers.PrimaryKeyRelatedField(
source='editorial',
queryset=Editorial.objects.all(),
write_only=True,)classMeta:
model = Libro
fields =['id','titulo','isbn','precio','editorial','editorial_id','autores']
Se escribe un número y se lee un objeto, que es lo que quiere todo el mundo. Y es la convención de nombres de la lección 11: el sufijo distingue el identificador del objeto.
El source='editorial' es la pieza que lo hace funcionar: dice que ese campo, aunque se llame editorial_id de cara afuera, escribe en el atributo editorial del modelo.
Si de verdad necesitas crear la editorial junto con el libro, se escribe el create a mano:
Y conviene pensarlo dos veces, porque una API que crea cosas de rebote es difícil de razonar: un error de escritura en el nombre de la editorial crea una editorial nueva en vez de dar un error.