La confiabilidad de una API inicia con datos limpios. Aquí verás, paso a paso, cómo implementar validaciones personalizadas en Django Rest Framework desde los serializers para controlar correos corporativos y reglas que combinan varios campos, generando mensajes de error claros en JSON y respuestas consistentes en el backend.
¿Cómo validar correos corporativos en Django Rest Framework?
La idea central es restringir el correo a un dominio específico, por ejemplo, @example.com, dentro del serializer del doctor. Aunque el campo email del modelo ya es de tipo EmailField y valida el formato, se necesita una validación custom de dominio. La estrategia es usar un método con nombre especial: validate_<campo>.
Usa el método validate_email para validar solo el campo email.
Retorna el valor si es válido. Lanza excepción si es inválido.
La excepción es serializers.ValidationError y DRF la muestra como JSON con estado 400.
Ejemplo basado en lo explicado:
from rest_framework import serializers
classDoctorSerializer(serializers.ModelSerializer):defvalidate_email(self, value):# valida que el dominio corporativo esté presenteif"example.com"in value:return value
raise serializers.ValidationError("el correo debe incluir @example.com")
Este método se activa automáticamente al correr el sistema de validación.
Si el valor es inválido, la respuesta será un 400 con el detalle del error.
También podrías pasar código o detalles extra en la excepción, pero aquí basta el texto.
¿Qué pasa si el correo no es válido?
Se lanza serializers.ValidationError con un mensaje claro.
El serializer serializa el error a JSON y lo muestra ligado al campo email.
El mensaje visto en pantalla es directo: "el correo debe incluir @example.com".
¿Cuándo usar validate para varias reglas de negocio?
Cuando la regla depende de más de un campo, se emplea el método validate(self, attrs). En el ejemplo, se cruza contact_number con is_on_vacation: si el doctor se marca en vacaciones sin un teléfono válido (mínimo 10 caracteres), se bloquea la actualización.
attrs es un diccionario con todos los valores validados por campo.
Accede con attrs.get("nombre_del_campo").
Lanza serializers.ValidationError para errores que no pertenecen a un único campo.
Ejemplo basado en lo explicado:
from rest_framework import serializers
classDoctorSerializer(serializers.ModelSerializer):defvalidate(self, attrs): contact_number = attrs.get("contact_number") is_on_vacation = attrs.get("is_on_vacation")if is_on_vacation and(not contact_number orlen(contact_number)<10):# error general: no asociado a un solo camporaise serializers.ValidationError("por favor, ingresa un número válido antes de irte a vacaciones")returnsuper().validate(attrs)
¿Cómo se muestran los errores en JSON?
Al subir reglas cruzadas a validate, DRF agrupa el error como non-field error.
El arreglo de mensajes aparece bajo la clave "non_field_errors".
En pruebas con PATCH, si todo es correcto, la respuesta es 200 ok y los cambios se reflejan.
¿Qué habilidades y keywords refuerzas con estas validaciones?
Aplicar estas técnicas mejora la calidad de datos y la experiencia de uso en endpoints sensibles.
Manejo de errores de validación con serializers.ValidationError y estado 400.
Validación custom con validate_<campo> para email corporativo.
Reglas de negocio cruzadas con validate(self, attrs) y errores tipo non-field errors.
Mensajes de error claros y consistentes en JSON.
Uso de booleanostrue/false y verificación de longitud mínima para teléfonos.
Buenas prácticas en serializer: retornar el valor si es válido y delegar a super().validate(attrs).
Se manejan principalmente dentro de los serializers.
Puedes hacer validaciones a nivel de campo o a nivel de objeto.
También puedes realizar validaciones basadas en el contexto.
Manejo de Errores:
DRF gestiona automáticamente muchas excepciones y devuelve respuestas claras.
Puedes personalizar el manejo de errores utilizando excepciones personalizadas o creando un manejador global de excepciones.
Utiliza raise_exception=True en los serializers para manejar automáticamente los errores de validación.
thanks for resume
Ventajas de manejar errores:
Mejora la experiencia del usuario: Proporciona mensajes claros y útiles cuando ocurren errores.
Aumenta la seguridad: Previene la exposición de información sensible a través de mensajes de error.
Facilita la depuración: Ayuda a los desarrolladores a identificar y solucionar problemas más rápidamente.
Ventajas de manejar validaciones:
Garantiza la integridad de los datos: Asegura que solo datos correctos y esperados sean procesados.
Previene errores: Reduce la posibilidad de errores en el sistema al validar la entrada de datos.
Mejora la seguridad: Evita la entrada de datos maliciosos que podrían comprometer el sistema.
perfect!
En Django REST Framework (DRF), el manejo de errores y las validaciones son componentes cruciales para crear APIs robustas. DRF facilita la creación de validaciones personalizadas y el manejo adecuado de errores, ya sea en los serializadores, vistas o directamente en las respuestas de la API.
### Validaciones en Serializers
Los **serializers** son el lugar donde generalmente ocurre la mayoría de las validaciones de datos. DRF proporciona métodos incorporados para validar datos en un ModelSerializer.
#### 1. Validaciones de Campos
DRF realiza validaciones automáticas basadas en los tipos de campo definidos en el modelo, pero también puedes agregar validaciones personalizadas en cada campo.
Ejemplo:
from rest\_framework import serializers
from.models import Patient
classPatientSerializer(serializers.ModelSerializer):  class Meta:  model = Patient  fields = '\_\_all\_\_'  \# Validación personalizada en un campo específico  def validate\_age(self, value):  if value < 0:  raise serializers.ValidationError("La edad no puede ser negativa.")  return value
#### 2. Validaciones Globales
Puedes realizar validaciones que afecten a múltiples campos a la vez usando el método validate en el serializador.
Ejemplo:
classPatientSerializer(serializers.ModelSerializer):  class Meta:  model = Patient  fields = '\_\_all\_\_'  \# Validación global (en múltiples campos)  def validate(self, data):  if data\['age'] < 18 and not data.get('guardian'):  raise serializers.ValidationError("Pacientes menores de edad deben tener un guardián.")  return data
### Manejo de Errores en Vistas
En Django REST Framework, puedes manejar errores y excepciones en las vistas, ya sea a través de excepciones incorporadas o personalizadas.
#### 1. Excepciones Incorporadas
DRF tiene una serie de excepciones predeterminadas que puedes usar, como:
- ValidationError: Para errores de validación.
- NotFound: Cuando un recurso no existe.
- PermissionDenied: Cuando el usuario no tiene los permisos necesarios.
- NotAuthenticated: Si se requiere autenticación pero no está presente.
Ejemplo:
from rest\_framework.exceptions import NotFound
from rest\_framework.views import APIView
from.models import Patient
from.serializers import PatientSerializer
classPatientDetailView(APIView):  def get(self, request, pk):  try:  patient = Patient.objects.get(pk=pk)  except Patient.DoesNotExist:  raise NotFound("El paciente con este ID no existe.")  serializer = PatientSerializer(patient)  return Response(serializer.data)
#### 2. Manejo de Excepciones Globales
Si quieres manejar los errores a nivel global, puedes personalizar el comportamiento utilizando un middleware o sobreescribiendo la función de excepción de DRF.
\# En settings.py
REST\_FRAMEWORK ={  'EXCEPTION\_HANDLER': 'my\_project.my\_app.utils.custom\_exception\_handler',}
Y luego defines tu función personalizada de manejo de errores:
from rest\_framework.views import exception\_handler
def custom\_exception\_handler(exc, context):  response = exception\_handler(exc, context)  if response is not None:  response.data\['status\_code'] = response.status\_code  response.data\['error'] = str(exc)  return response
### Manejo de Validaciones Personalizadas en Formularios
También puedes agregar validaciones personalizadas usando el atributo validators en los serializadores para campos específicos.
from rest\_framework import serializers
from django.core.validators import RegexValidator
classPatientSerializer(serializers.ModelSerializer):  phone\_number = serializers.CharField(  validators=\[RegexValidator(regex='^(\\+)?\[0-9]{10,15}$', message="Número de teléfono inválido")]  )     class Meta:  model = Patient  fields = '\_\_all\_\_'
### Respuestas Personalizadas en Caso de Error
Puedes crear respuestas personalizadas cuando ocurre un error en la validación de los datos en tu API. Por ejemplo, puedes sobrescribir cómo los errores son mostrados en las respuestas.
from rest\_framework.response import Response
from rest\_framework import status
from rest\_framework.views import APIView
classPatientDetailView(APIView):  def post(self, request):  serializer = PatientSerializer(data=request.data)  if serializer.is\_valid():  serializer.save()  return Response(serializer.data, status=status.HTTP\_201\_CREATED)  return Response({  'status': 'error',  'message': 'Datos inválidos',  'errors': serializer.errors  }, status=status.HTTP\_400\_BAD\_REQUEST)
### Resumen
- **Validaciones en Serializers**: Usa validate\_\<field> para validaciones de campo, y el método validate para validaciones que involucran múltiples campos.
- **Manejo de Excepciones**: Puedes usar excepciones predefinidas o personalizadas en tus vistas.
- **Respuestas de Error Personalizadas**: Puedes sobrescribir el formato de los errores de validación para personalizar las respuestas JSON.
- **Excepciones Globales**: Puedes definir un manejador de excepciones global para todo el proyecto a través de la configuración de EXCEPTION\_HANDLER.
Estos mecanismos te permitirán gestionar errores y validaciones de manera eficiente en tu proyecto Django REST Framework.
cool!
Las principales validaciones que yo vi necesarias para los modelos de pacientes son validacion del numero de contacto como maximo de caracteres y que solo deben haber numeros y en Insurance validacion del policy number y expiration date
from rest_framework import serializers
from .models import Patient, Insurance, MedicalRecord
from datetime import datetime
class PatientSerializers(serializers.ModelSerializer):
class Meta:
model=Patient
fields = '__all__'
def validate_contact_number(self, value):
if len(value) > 10:
raise serializers.ValidationError("the maximium number of digits in a policy number is 10")
if not value.isdigit():
raise serializers.ValidationError("The contact number only contain numbers")
return value
def validate(self, attrs):
if attrs['first_name'].casefold() == attrs['last_name'].casefold():
raise serializers.ValidationError("The first name and last name can't be iguals")
return super().validate(attrs)
class InsuranceSerializers(serializers.ModelSerializer):
class Meta:
model=Insurance
fields = '__all__'
def validate_policy_number(self, value):
if value.isdigit():
return value
else:
raise serializers.ValidationError("The maximium number of digits in a policy number is 10")
def validate_expiration_date(self, value):
fecha_hoy = datetime.now().date()
if fecha_hoy <value:
return value
else:
raise serializers.ValidationError("The expiration date cant be before than today date")
class MedicalRecordSerializers(serializers.ModelSerializer):
class Meta:
model=MedicalRecord
fields = '__all__'
Muy buen progreso, una recomendación extra: puedes usar una lista en fields en vez de '__all__' para evitar que se incluyan nuevos campos auotmaticamente.
Si utilizamos el método set_on_vacation que creamos anteriormente no se realiza la validación. Como podemos solucionar esto?
classPatientSerializer(serializers.ModelSerializer):classMeta: model =Patient fields ="__all__" def validate_email(self, value):if"@example.com"invalue:return value
raise serializers.ValidationError("Email should contain @example.com") def validate(self, attrs):iflen(attrs["contact_number"])<10: raise serializers.ValidationError("Contact number must be at least 10 digits")if not attrs["medical_history"] or len(attrs["medical_history"])<20: raise serializers.ValidationError("Medical history must be at least 20 characters")if attrs["date_of_birth"]> datetime.now().date(): raise serializers.ValidationError("Date of birth cannot be in the future")returnsuper().validate(attrs)
Validación para la Edad del Paciente
classPatientSerializer(serializers.ModelSerializer):classMeta: model =Patient fields ="__all__" def validate_date_of_birth(self, value): today = datetime.date.today() age = today.year- value.year-((today.month, today.day)<(value.month, value.day))if age >=18:return value
raise serializers.ValidationError('El paciente debe tener al menos 18 años')
Comparto mi solución al reto:
Validando que no se usen caracteres especiales en el campo de nombre y apellido, además que el número de contacto no sea un número superior a 10.
classPatientSerializer(serializers.ModelSerializer):classMeta: model = Patient
fields ="__all__"defvalidate(self, attrs): patron =r'^[a-zA-ZáéíóúÁÉÍÓÚñÑüÜ\s\-]+$'ifnot re.match(patron, attrs['first_name'])ornot re.match(patron, attrs['last_name']):raise serializers.ValidationError("The first name and last name not must be special character")iflen(attrs["contact_number"])>10:raise serializers.ValidationError("Contact number must be at least 10 digits")returnsuper().validate(attrs)