Serializadores anidados en Django REST Framework

Resumen

Los serializadores anidados en Django REST Framework te permiten incluir el resultado de un serializador dentro de otro, enriqueciendo las respuestas de tu API con información relacionada. Aprenderás a mostrar las citas médicas de un paciente dentro del mismo JSON, algo útil si construyes APIs REST con relaciones entre modelos.

En la aplicación Doctor App, vamos a anidar el AppointmentSerializer dentro del PatientSerializer para que cada paciente exponga su lista de citas en una sola respuesta.

¿Qué son los serializadores anidados y para qué sirven?

Un serializador anidado es un campo dentro de un serializador cuyo valor proviene de otro serializador. En lugar de devolver solo IDs o campos planos, devuelves objetos completos relacionados.

¿Qué es un serializador anidado en DRF? Es un campo dentro de un serializer que usa otro serializer como tipo de dato, permitiendo incluir objetos relacionados completos en la respuesta JSON.

La ventaja práctica es la centralización de cambios: si modificas el serializador hijo, todas las respuestas que lo usan se actualizan al mismo tiempo. Eso te ahorra duplicar lógica entre endpoints.

¿Cómo agregar un serializador anidado al PatientSerializer?

Dentro de la carpeta patients ubicas el PatientSerializer y trabajas sobre la lista fields y los campos declarados en la clase. La idea es declarar appointments como atributo y luego registrarlo en fields.

El flujo es así:

  1. Importa el serializador hijo con from bookings.serializers import AppointmentSerializer.
  2. Declara el campo: appointments = AppointmentSerializer(many=True, read_only=True).
  3. Agrega 'appointments' dentro de la lista fields del Meta.

El parámetro many=True indica que el campo procesará varios elementos del mismo tipo, ideal para una lista de citas. El parámetro read_only=True define el campo como solo lectura, así nadie podrá enviar citas en un POST a /patients, porque ese flujo pertenece a otro endpoint.

¿Para qué sirve read_only=True en un serializer? Marca el campo como solo lectura, evitando que datos enviados en POST o PUT sean procesados o guardados desde ese endpoint.

¿Cómo busca DRF los campos declarados?

El orden de búsqueda importa: primero revisa los atributos declarados en la clase del serializer, después los campos del modelo, y si no encuentra coincidencia, lanza un error. Por eso el campo appointments debe existir como atributo de clase antes de listarlo en fields.

Un detalle fácil de pasar por alto: cuando copias los campos directamente desde el modelo, puedes olvidar el id, porque ese campo se crea automáticamente al heredar de models.Model. Si tu detalle deja de mostrarlo, agrégalo manualmente en fields.

¿Cómo se ve la respuesta JSON con datos reales?

Al ejecutar manage.py runserver y entrar a /api/patients/, el listado y el detalle muestran el nuevo campo appointments. Si el paciente no tiene citas creadas, verás un array vacío.

Para poblar datos rápido, usa el shell interactivo con manage.py shell y crea registros directamente desde el manager del modelo Appointment. El proceso típico:

  • Importa Doctor, Patient, Appointment y los módulos de fecha.
  • Define el paciente buscándolo por id=3.
  • Toma cualquier doctor existente con Doctor.objects.first().
  • Llama a Appointment.objects.create(...) pasando paciente, doctor, fecha, hora y opcionalmente notas o estatus.

Al recargar el detalle del paciente, el campo appointments deja de ser un array vacío y devuelve una lista de objetos JSON con la estructura definida por AppointmentSerializer. Ahí ves el poder real del anidamiento: una sola petición trae paciente y citas juntos.

¿Cuándo conviene usar serializadores anidados?

Funcionan bien cuando quieres exponer relaciones de lectura en respuestas ricas, como un paciente con sus citas, un pedido con sus productos o un curso con sus lecciones. No los uses para escritura compleja sin configurar manualmente los métodos create y update, porque por defecto read_only=True bloquea la escritura.

Algunos usos típicos en una API REST:

  • Mostrar recursos hijos dentro del padre sin múltiples llamadas.
  • Reducir la cantidad de requests desde el cliente.
  • Mantener consistencia visual del JSON entre listado y detalle.

Ahora que ya manejas el patrón, replica el ejercicio con el DoctorSerializer: agrega un campo appointments que devuelva todas las citas asignadas a cada doctor. Cuéntame en los comentarios cómo resolviste el campo y si decidiste filtrar las citas por estatus o fecha.