Optimiza tu API en Django REST Framework con un endpoint que lista y crea pacientes en la misma URL, valida datos con serializadores y responde con códigos HTTP claros como 201 Created y 404 Not Found. Aprende a usar la variable request, el atributo method y a estructurar URLs por aplicación con include y parámetros int:pk.
¿Cómo crear y listar pacientes en un endpoint REST con GET y POST?
Un mismo recurso puede listar con GET y crear con POST usando la misma ruta. La clave está en leer request.method y aplicar un condicional por método para ejecutar la lógica correcta.
¿Qué hace la misma URL según el método?
En GET: se consulta la base de datos y se serializa con many=True.
En POST: se recibe request.data y se intenta crear un paciente.
La variable request expone el atributo method para decidir la acción.
¿Cómo validar y guardar con el serializador?
Se instancia el serializador con el parámetro data=request.data.
Se valida con isValid (en DRF se usa con excepciones para manejar errores automáticamente).
Si es válido, se guarda con serializer.save() como si fuera un ModelSerializer.
Se puede retornar sin body y comunicar éxito solo con status=HTTP_201_CREATED.
Los campos de solo lectura (como id) no se toman del body: el backend asigna el valor.
¿Cómo manejar errores y validaciones con serializadores y códigos HTTP?
Validar entradas del usuario es crítico. Un dato inválido (por ejemplo, una fecha con formato incorrecto) no debe causar un 500, sino una respuesta JSON con errores de campo.
¿Qué pasa con datos inválidos y raisedException?
Al usar raisedException en la validación, DRF captura los errores y los formatea en JSON.
El cliente recibe el detalle del campo y el formato esperado, evitando caídas del servidor.
¿Por qué eliminar el if tras las excepciones?
Si se lanza una excepción de validación, el flujo se detiene.
Por eso, se puede omitir el if “si es válido” y dejar la secuencia: validar → guardar → responder.
¿Cómo implementar detalle y rutas en Django con include y parámetros?
Para el detalle, el mismo recurso admite ver con GET y modificar con PUT (el reto propuesto). Es necesario capturar el pk desde la URL y manejar el caso de un paciente inexistente.
¿Cómo obtener un paciente o responder 404 not found?
Intentar con get y capturar la excepción Patient.DoesNotExist.
@api_view(["PUT"])defupdate_patient(request, pk):try:# Look for the patient by their primary key (pk) patient = Patient.objects.get(pk=pk)except Patient.DoesNotExist:# If the patient does not exist, return a 404 errorreturn Response({'error':'Patient not found'}, status=status.HTTP_404_NOT_FOUND)# Deserialize the incoming request data serializer = PatientSerializer(patient, data=request.data)# Validate the incoming dataif serializer.is_valid():# Save the changes if the data is valid serializer.save()# Return the updated patient datareturn Response(serializer.data)# If the data is not valid, return the errorsreturn Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
ty
get_object_or_404 para manejar mejor los casos donde el paciente no existe, y evitar que se rompa la aplicación:
Depende también del caso, puede que no quieras mostrar un 404.
¿No es recomendable que cada función tenga una responsabilidad específica? Es una buena práctica tener los métodos GET y POST en una misma función?
Puesto que django rest abstrae bastante bien la lógica de ambas cosas está bien tenerla en la misma parte, dependiendo de que tan ordenado estes pues "omitir" ciertas reglas, en este caso diria que no se omite porque la unica funcionalidad de ese method es "recibir el request" y enviarlo a procesar en otros metodos internos.
EL codigo del reto:
(spoiler, para actualizar el usuario se debe actualizar toda la informacion, esto debido a que todos los campos estan obligatorios en el modelo)
Este código me quedó cómodo pero prueben ustedes qué les parece:
# Importaciones necesarias
from rest_framework.decoratorsimportapi_viewfrom rest_framework.responseimportResponsefrom rest_framework importstatusfrom.modelsimportPatient # Importar el modelo Patientfrom.serializersimportPatientSerializer # Importar el serializer del modelo
# 1.Método GET:Obtener todos los pacientes
@api_view(["GET"])def list_patients(request): patients =Patient.objects.all() # Obtener todos los pacientes
serializer =PatientSerializer(patients, many=True) # Serializar los datos
returnResponse(serializer.data) # Devolver los datos en formato JSON# 2.Método POST:Crear un nuevo paciente
@api_view(["POST"])def create_patient(request): serializer =PatientSerializer(data=request.data) # Deserializar los datos del cliente
if serializer.is_valid(): # Validar los datos
serializer.save() # Guardar el nuevo paciente
returnResponse(serializer.data, status=status.HTTP_201_CREATED) # Respuesta exitosa
returnResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST) # Devolver errores si los datos no son válidos
# 3.Método PUT:Actualizar un paciente existente
@api_view(["PUT"])def update_patient(request, pk):try: patient =Patient.objects.get(pk=pk) # Obtener el paciente por su ID(pk) except Patient.DoesNotExist:returnResponse({'error':'Paciente no encontrado'}, status=status.HTTP_404_NOT_FOUND) serializer =PatientSerializer(patient, data=request.data) # Deserializar y actualizar con los nuevos datos
if serializer.is_valid(): serializer.save() # Guardar los cambios
returnResponse(serializer.data) # Respuesta con los datos actualizados
returnResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST) # Devolver errores si los datos no son válidos
# 4.Método DELETE:Eliminar un paciente
@api_view(["DELETE"])def delete_patient(request, pk):try: patient =Patient.objects.get(pk=pk) # Obtener el paciente por su ID(pk) except Patient.DoesNotExist:returnResponse({'error':'Paciente no encontrado'}, status=status.HTTP_404_NOT_FOUND) patient.delete() # Eliminar el paciente
returnResponse({'message':'Paciente eliminado correctamente'}, status=status.HTTP_204_NO_CONTENT)
hola fernando y como te quedaron las urls ? tengo la duda si una urlpattern puede soportar multiples views creo que no, saludos; tambien me gusta separar cada metodo http en su propia vista
les comparto un ejemplo de paciente para que no tengan que escribirlo desde cero
{"id":1,"first_name":"Mr. Donas","last_name":"Chocolate","date_of_birth":"1998-07-30","contact_number":"555-546-5655","email":"Delicuos_donas@gmail.com","address":"123 Bakery st, NewHampshire, USA","medical_history":"Diabetes, treated with insulin"}
Mi solución al reto:@api_view(["GET", "PUT"])def patient(request, pk, format=None): match request.method: case "GET": data = None status = HTTP_200_OK
try: patient = Patient.objects.get(id=pk) serializer = PatientSerializer(patient) data = serializer.data except Patient.DoesNotExist: data = {"error": "Patient not found"} status = HTTP_404_NOT_FOUND finally: return Response(data, status=status)
case "PUT": data = None status = HTTP_200_OK
try: patient = Patient.objects.get(id=pk) serializer = PatientSerializer(patient, data=request.data, partial=True) serializer.is_valid(raise_exception=True) serializer.save() data = serializer.data except Patient.DoesNotExist: data = {"error": "Patient not found"} status = HTTP_404_NOT_FOUND finally: return Response(data, status=status)
@api_view(["GET","PUT"])defpatient(request, pk,format=None):match request.method:case"GET": data =None status = HTTP_200_OK
try: patient = Patient.objects.get(id=pk) serializer = PatientSerializer(patient) data = serializer.data
except Patient.DoesNotExist: data ={"error":"Patient not found"} status = HTTP_404_NOT_FOUND
finally:return Response(data, status=status)case"PUT": data =None status = HTTP_200_OK
try: patient = Patient.objects.get(id=pk) serializer = PatientSerializer(patient, data=request.data, partial=True) serializer.is_valid(raise_exception=True) serializer.save() data = serializer.data
except Patient.DoesNotExist: data ={"error":"Patient not found"} status = HTTP_404_NOT_FOUND
finally:return Response(data, status=status)
importante si cuando usando el metodo 'PUT' solo le mandan un campo, por ejemplo:patient_serializer = PatientSerializer(patient, data=request.data, partial=True)
{"first_name":"Nuevo Nombre"}```van a recibir error, porque los serializers de django siempre espera que lleguen todos los campos segun las condiciones en la que se hayan creado sus campos, en este caso similar al Modelo, porque usamos ModelSerializer, asi que para actualizer un atributo o varios de forma parcial deben usar el serializer asi:
```python
patient_serializer =PatientSerializer(patient, data=request.data, partial=True)```y si sera valido, de caso contrario no lo es. 
Gracias por este aporte, demasiado útil el partial True
No estoy tan acostumbrado de escribir código así, o por lo menos el backend, si no tener una api por cada operación, pero lo simpliqué a ver como se ve con python, no se nada mal:
Entre los códigos HTTP más comunes se encuentran el 200 (OK), que indica que la solicitud ha sido exitosa; el 404 (Not Found), que señala que el recurso solicitado no se ha encontrado; el 500 (Internal Server Error), que indica un error en el servidor; el 301 (Moved Permanently), que informa que el recurso ha sido movido de forma permanente a una nueva URL; y el 302 (Found), que señala una redirección temporal.
Estos códigos ayudan a diagnosticar y solucionar problemas en la navegación web, asegurando una experiencia más fluida para los usuarios.