El componente Forms de Django es una herramienta fundamental para gestionar la entrada de datos del usuario, ya sea para registro, inicio de sesión o cualquier otra interacción que requiera datos del frontend. Permite validar la información recibida, generar campos HTML y mantener los valores introducidos previamente.
Campos y Widgets Comunes en Forms
Django proporciona una rica variedad de campos y widgets para definir la estructura y el comportamiento de los formularios:
- Field: Clase base para todos los campos.
required: Indica si el campo es obligatorio.widget: El componente HTML que se usará para el campo (ej. TextInput, PasswordInput).label: El texto de la etiqueta asociada al campo.initial: Valor predeterminado para el campo.help_text: Mensaje de ayuda contextual.error_messages: Diccionario para personalizar mensajes de error.validators: Lista de validadores personalizados.
- CharField: Campo de texto simple.
max_length,min_length: Define la longitud máxima y mínima del texto.strip: Elimina los espacios en blanco al principio y al final del texto.
- IntegerField, FloatField, DecimalField: Campos para números, con validaciones de rango y precisión.
- DateField, TimeField, DateTimeField: Campos para fechas y horas, con formatos de entrada configurables.
- RegexField: Campo que valida el contenido contra una expresión regular.
- EmailField: Valida que la entrada sea una dirección de correo electrónico válida.
- FileField, ImageField: Campos para la carga de archivos e imágenes. Requieren
enctype="multipart/form-data"en el formulario HTML y el manejo derequest.FILESen la vista. - URLField: Valida que la entrada sea una URL válida.
- BooleanField, NullBooleanField: Campos booleanos.
- ChoiceField, TypedChoiceField: Campos con opciones predefinidas (ej. dropdowns, radio buttons).
- MultipleChoiceField, TypedMultipleChoiceField: Permiten la selección de múltiples opciones.
- ModelChoiceField, ModelMultipleChoiceField: Campos que obtienen sus opciones directamente de un QuerySet de modelo.
- SplitDateTimeField: Combina campos de fecha y hora.
- FilePathField: Permite seleccionar un archivo de un directorio específico.
- GenericIPAddressField: Valida direcciones IP.
- SlugField: Campo para slugs (texto usado en URLs).
- UUIDField: Campo para valores UUID.
Validación de Datos con Forms
La validación es una de las funciones principales de Django Forms. Se define la estructura del formulario en una clase que hereda de django.forms.Form.
Ejemplo Básico de Validación
from django import forms
class RegistroForm(forms.Form):
nombre = forms.CharField(
required=False,
max_length=32,
min_length=3,
label='Nombre de Usuario'
)
email = forms.EmailField(label='Correo Electrónico')
edad = forms.IntegerField(max_value=200, min_value=0, label='Edad')
# En la vista (views.py):
from django.shortcuts import render
from .forms import RegistroForm
def registrar(request):
if request.method == 'POST':
form = RegistroForm(request.POST)
if form.is_valid():
# Los datos validados están en form.cleaned_data
print("Validación exitosa:", form.cleaned_data)
# Procesar los datos (ej. guardar en la base de datos)
else:
# Los errores de validación están en form.errors
print("Validación fallida:", form.errors)
else:
form = RegistroForm() # Formulario GET inicial
return render(request, 'registro.html', {'form': form})
El método form.is_valid() ejecuta todas las validaciones. Si es True, los datos limpios y validados se encuentran en form.cleaned_data. Si es False, los detalles de los errores están en form.errors.
Configuración de Parámetros y Widgets
Los campos de formulario se pueden personaliazr extensamente:
from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError
class FormularioPersonalizado(forms.Form):
nombre = forms.CharField(
required=False,
max_length=32,
min_length=3,
label='Nombre de Usuario',
widget=widgets.TextInput(attrs={'class': 'form-control'}),
error_messages={'min_length': 'El nombre es demasiado corto.'}
)
password = forms.CharField(
required=False,
max_length=32,
min_length=3,
label='Contraseña',
widget=widgets.PasswordInput(attrs={'class': 'form-control'})
)
email = forms.EmailField(
label='Correo Electrónico',
widget=widgets.TextInput(attrs={'class': 'form-control'}),
error_messages={'required': 'El correo es obligatorio.'}
)
fecha = forms.DateField(
label='Fecha',
widget=forms.DateInput(attrs={'type': 'date', 'class': 'form-control'})
)
# Validación personalizada para un campo específico
def clean_nombre(self):
nombre = self.cleaned_data.get('nombre')
if nombre and nombre.startswith('admin'):
raise ValidationError('El nombre no puede comenzar con "admin".')
return nombre
# Validación global para todo el formulario
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get("password")
confirmar_password = self.data.get("confirmar_password") # Asumiendo que hay un campo confirmar_password
if password and confirmar_password and password != confirmar_password:
raise ValidationError("Las contraseñas no coinciden.")
return cleaned_data
Uso de Validadores
Se pueden usar validadores predefinidos o crear los propios.
RegexValidator
from django.forms import Form, fields
from django.core.validators import RegexValidator
class FormularioRegex(Form):
usuario = fields.CharField(
validators=[
RegexValidator(r'^[0-9]+$', 'Debe ingresar solo números.'),
RegexValidator(r'^159[0-9]+$', 'El número debe comenzar con 159.')
]
)
Validadores Personalizados
Se pueden definir funciones de validación y pasarlas a la lista validators de un campo.
import re
from django.forms import Form, fields, widgets
from django.core.exceptions import ValidationError
def validar_telefono(value):
patron_movil = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
if not patron_movil.match(value):
raise ValidationError('Formato de teléfono móvil inválido.')
class FormularioTelefono(Form):
telefono = fields.CharField(
validators=[validar_telefono],
error_messages={'required': 'El teléfono es obligatorio.'},
widget=widgets.TextInput(attrs={'placeholder': 'Número de teléfono'})
)
Renderizado de Formularios en Plantillas
Django facilita la inclusión de formularios en plantillas HTML.
Plantilla Manual
<form method="post">
{% csrf_token %}
<p>Nombre: <input type="text" name="nombre"></p>
<p>Email: <input type="text" name="email"></p>
<p>Edad: <input type="text" name="edad"></p>
<button type="submit">Enviar</button>
</form>
Renderizado Semi-Automático
Se puede renderizar cada campo individualmente o iterando sobre el formulario.
Iterando sobre el formulario
<form method="post">
{% csrf_token %}
{% for campo in form %}
<p>{{ campo.label }}: {{ campo }} {% if campo.errors %}<span class="error">{{ campo.errors }}</span>{% endif %}</p>
{% endfor %}
<button type="submit">Enviar</button>
</form>
Renderizado Totalmente Automático
Django ofrece atajos para renderizar el formulario completo en diferentes formatos.
<form method="post">
{% csrf_token %}
{{ form.as_p }} {# Renderiza como párrafos #}
{# {{ form.as_ul }} Renderiza como lista no ordenada #}
{# {{ form.as_table }} Renderiza como tabla #}
<button type="submit">Enviar</button>
</form>
Para mostrar errores de validación, se accede a form.errors o a los errores de cada campo individualmente (campo.errors).
Métodos Hook (Ganchos)
Permiten añadir validaciones personalizadas en diferentes etapas del proceso de validación.
Hook Local (clean_nombreCampo)
Se define un método con el nombre clean_ seguido del nombre del campo. Se ejecuta después de la validación estándar del campo.
Hook Global (clean)
Este método se ejecuta después de que todos los campos individuales han sido validados. Es ideal para validaciones que dependen de múltiples campos.
class LoginForm(forms.Form):
nombre_usuario = forms.CharField(min_length=8)
contrasena = forms.CharField(min_length=6)
confirmar_contrasena = forms.CharField(min_length=6)
def clean_nombre_usuario(self):
valor = self.cleaned_data.get('nombre_usuario')
if "test" in valor:
raise ValidationError("El nombre de usuario no puede contener 'test'.")
return valor
def clean(self):
datos = super().clean()
contrasena = datos.get('contrasena')
confirmar_contrasena = datos.get('confirmar_contrasena')
if contrasena and confirmar_contrasena and contrasena != confirmar_contrasena:
self.add_error('confirmar_contrasena', 'Las contraseñas no coinciden.')
# Alternativamente, raise ValidationError('Las contraseñas no coinciden.')
return datos
ModelForm
ModelForm es una clase especializada que crea un formulario directamente a partir de un modelo de Django. Simplifica la creación de formularios para operaciones CRUD (Crear, Leer, Actualizar, Borrar).
Creación de un ModelForm
from django import forms
from .models import Estudiante
class FormularioEstudiante(forms.ModelForm):
class Meta:
model = Estudiante
fields = "__all__" # O una lista de campos: ['nombre', 'edad']
# exclude = ['campo_a_excluir']
labels = {
'nombre': 'Nombre Completo',
'edad': 'Edad del Estudiante'
}
widgets = {
'nombre': forms.TextInput(attrs={'class': 'form-control'}),
'descripcion': forms.Textarea(attrs={'rows': 3})
}
error_messages = {
'nombre': {'required': 'El nombre es obligatorio.'}
}
# En la vista:
def crear_estudiante(request):
if request.method == 'POST':
form = FormularioEstudiante(request.POST, request.FILES) # Incluir request.FILES si hay subida de archivos
if form.is_valid():
form.save() # Guarda automáticamente la instancia del modelo
return redirect('lista_estudiantes')
else:
form = FormularioEstudiante()
return render(request, 'estudiante_form.html', {'form': form})
Edición de Datos con ModelForm
Para editar un registro existente, se pasa la instancia del modelo al formulario.
def editar_estudiante(request, pk):
estudiante = Estudiante.objects.get(pk=pk)
if request.method == 'POST':
form = FormularioEstudiante(request.POST, request.FILES, instance=estudiante)
if form.is_valid():
form.save()
return redirect('lista_estudiantes')
else:
form = FormularioEstudiante(instance=estudiante)
return render(request, 'estudiante_form.html', {'form': form})
Análisis del Código Fuente de Forms
El método full_clean() es el corazón del proceso de validación. Invoca primero _clean_fields() para validar campos individualmente (incluyendo los hooks locales como clean_nombre), y luego _clean_form() para ejecutar el hook global clean.
_clean_fields() itera sobre cada campo, obtiene su valor usando el widget correspondiente, llama al método clean() del campo (que a su vez puede llamar a validadores y hooks locales), y almacena el resultado en cleaned_data si la validación es exitosa. Si ocurre una ValidationError, se añade al diccionario _errors del formulario.
_clean_form() ejecuta el método clean() del formulario. Si este método devuelve un diccionario, éste reemplaza el cleaned_data actual. Si se produce una ValidationError en el método clean() global, se añade a los errores del formulario.