Niveles de validación en Django
Django aplica la validación en varios niveles de forma secuencial:

- Validación del campo (
clean_nombre_campo()): válida el valor de un campo específico. - Validación del formulario (
clean()): válida relaciones entre varios campos. - Validación del modelo (si se usa
ModelForm): aplica las restricciones del modelo.
Validación de campo individual
El método clean_<nombre_campo>() se llama automáticamente para cada campo y debe devolver el valor limpio o lanzar ValidationError:
from django import forms
from django.core.exceptions import ValidationError
from .models import Usuario
class RegistroForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
confirmar_password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = Usuario
fields = ['username', 'email', 'password']
def clean_username(self):
username = self.cleaned_data.get('username')
if len(username) < 4:
raise ValidationError('El nombre de usuario debe tener al menos 4 caracteres.')
if Usuario.objects.filter(username__iexact=username).exists():
raise ValidationError(f'El nombre de usuario "{username}" ya está en uso.')
return username.lower() # Normalizar a minúsculas
def clean_email(self):
email = self.cleaned_data.get('email')
dominio = email.split('@')[1] if '@' in email else ''
dominios_bloqueados = ['tempmail.com', 'guerrillamail.com', '10minutemail.com']
if dominio in dominios_bloqueados:
raise ValidationError('No se permiten correos temporales.')
return email.lower()
Validación cruzada con clean()
El método clean() recibe todos los campos ya validados individualmente y permite validaciones que involucran múltiples campos:
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get('password')
confirmar_password = cleaned_data.get('confirmar_password')
if password and confirmar_password:
if password != confirmar_password:
# Error no asociado a un campo específico (aparece en form.non_field_errors())
raise ValidationError('Las contraseñas no coinciden.')
if len(password) < 8:
self.add_error('password', 'La contraseña debe tener al menos 8 caracteres.')
return cleaned_data
Para asociar el error a un campo específico dentro de clean(), usa self.add_error('nombre_campo', mensaje).
Validators reutilizables
Los validators son funciones o clases reutilizables que se pueden aplicar a múltiples campos y formularios:
from django.core.validators import RegexValidator, MinValueValidator, MaxValueValidator
from django.core.exceptions import ValidationError
# Validador como función
def validar_codigo_postal(valor):
if not valor.isdigit() or len(valor) != 5:
raise ValidationError(
'%(value)s no es un código postal válido. Debe tener 5 dígitos.',
params={'value': valor}
)
# Validador como clase (reutilizable con configuración)
class ValidadorEdadMinima:
def __init__(self, edad_minima):
self.edad_minima = edad_minima
def __call__(self, valor):
if valor < self.edad_minima:
raise ValidationError(
f'Debes tener al menos {self.edad_minima} años para registrarte.'
)
def deconstruct(self):
return (
f'{self.__class__.__module__}.{self.__class__.__name__}',
[self.edad_minima],
{}
)
# RegexValidator integrado
validar_telefono = RegexValidator(
regex=r'^\+?[1-9]\d{8,14}$',
message='Número de teléfono no válido. Formato: +34123456789',
code='telefono_invalido'
)
Aplicar validators en el modelo o en el formulario:
class PerfilForm(forms.ModelForm):
codigo_postal = forms.CharField(validators=[validar_codigo_postal])
telefono = forms.CharField(validators=[validar_telefono])
edad = forms.IntegerField(
validators=[
MinValueValidator(18, message='Debes ser mayor de edad.'),
MaxValueValidator(120, message='Edad no válida.'),
ValidadorEdadMinima(18)
]
)
Mostrar errores en plantillas
Django proporciona varias formas de renderizar errores en plantillas:
<form method="post">
{% csrf_token %}
<!-- Errores globales del formulario (non_field_errors) -->
{% if form.non_field_errors %}
<div class="alert alert-danger">
{% for error in form.non_field_errors %}
<p>{{ error }}</p>
{% endfor %}
</div>
{% endif %}
<!-- Renderizado campo por campo con errores -->
{% for field in form %}
<div class="campo {% if field.errors %}tiene-error{% endif %}">
{{ field.label_tag }}
{{ field }}
{% if field.errors %}
<ul class="errores">
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
{% if field.help_text %}
<small>{{ field.help_text }}</small>
{% endif %}
</div>
{% endfor %}
<button type="submit">Registrarse</button>
</form>
Formulario de contacto completo
class FormularioContacto(forms.Form):
nombre = forms.CharField(
max_length=100,
min_length=2,
error_messages={
'min_length': 'El nombre debe tener al menos 2 caracteres.',
'required': 'El nombre es obligatorio.',
}
)
email = forms.EmailField(
error_messages={'invalid': 'Introduce una dirección de correo válida.'}
)
asunto = forms.ChoiceField(
choices=[
('', '--- Selecciona un asunto ---'),
('consulta', 'Consulta general'),
('soporte', 'Soporte técnico'),
('facturacion', 'Facturación'),
]
)
mensaje = forms.CharField(
widget=forms.Textarea(attrs={'rows': 5}),
min_length=20,
max_length=1000
)
acepto_politica = forms.BooleanField(
error_messages={'required': 'Debes aceptar la política de privacidad.'}
)
def clean_asunto(self):
asunto = self.cleaned_data.get('asunto')
if not asunto:
raise ValidationError('Por favor, selecciona un asunto.')
return asunto
La validación multinivel de Django garantiza que los datos que llegan a cleaned_data cumplen todas las reglas definidas, simplificando la lógica de las vistas al tener la certeza de que solo se procesa cuando form.is_valid() devuelve True.
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Django es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Django
Explora más contenido relacionado con Django y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Implementar validación de campo individual con métodos clean_campo(). Usar el método clean() para validación cruzada entre campos del formulario. Crear validadores reutilizables con la clase Validator y funciones validadoras. Aplicar RegexValidator y otros validadores integrados de Django. Mostrar errores de validación correctamente en las plantillas.