Uso de st.session_state para mantener datos
Las aplicaciones web son inherentemente sin estado, lo que significa que cada vez que un usuario interactúa con la interfaz, la aplicación se reinicia completamente. En Streamlit, esto presenta un desafío cuando necesitamos preservar información entre las diferentes ejecuciones del script.
El objeto st.session_state
es la solución nativa de Streamlit para mantener datos persistentes durante toda la sesión del usuario. Funciona como un diccionario Python que conserva sus valores mientras el usuario mantenga abierta la pestaña del navegador.
Inicialización básica de variables de estado
La forma más común de trabajar con st.session_state
es inicializar variables al comienzo de la aplicación para evitar errores cuando se accede a ellas por primera vez:
import streamlit as st
# Inicializar el contador si no existe
if 'contador' not in st.session_state:
st.session_state.contador = 0
# Mostrar el valor actual
st.write(f"Valor actual del contador: {st.session_state.contador}")
Esta técnica es fundamental porque garantiza que la variable exista antes de intentar usarla, evitando errores de clave no encontrada.
Operaciones básicas con session_state
Puedes realizar todas las operaciones típicas de un diccionario Python con st.session_state
:
# Asignación directa
st.session_state.mi_variable = "Hola mundo"
# Verificar existencia
if 'mi_lista' not in st.session_state:
st.session_state.mi_lista = []
# Añadir elementos a una lista
st.session_state.mi_lista.append("nuevo elemento")
# Acceso mediante notación de diccionario
st.session_state['usuario_activo'] = True
# Eliminar una clave
if 'variable_temporal' in st.session_state:
del st.session_state.variable_temporal
Creación de un contador interactivo
Un ejemplo práctico para entender el comportamiento de st.session_state
es implementar un contador que mantenga su valor entre interacciones:
import streamlit as st
# Inicialización del contador
if 'contador' not in st.session_state:
st.session_state.contador = 0
st.title("Contador Persistente")
# Mostrar el valor actual
st.metric("Contador", st.session_state.contador)
# Crear columnas para los botones
col1, col2, col3 = st.columns(3)
with col1:
if st.button("➕ Incrementar"):
st.session_state.contador += 1
with col2:
if st.button("➖ Decrementar"):
st.session_state.contador -= 1
with col3:
if st.button("🔄 Reiniciar"):
st.session_state.contador = 0
Cada vez que el usuario pulsa un botón, el valor se actualiza y se mantiene visible hasta que realice otra acción.
Almacenamiento de datos complejos
st.session_state
puede almacenar cualquier tipo de dato Python, incluyendo estructuras complejas como listas, diccionarios o incluso objetos personalizados:
# Inicializar una lista de tareas
if 'tareas' not in st.session_state:
st.session_state.tareas = []
# Inicializar configuración de usuario
if 'configuracion' not in st.session_state:
st.session_state.configuracion = {
'tema': 'claro',
'idioma': 'es',
'notificaciones': True
}
st.title("Gestión de Tareas")
# Añadir nueva tarea
nueva_tarea = st.text_input("Nueva tarea:")
if st.button("Añadir") and nueva_tarea:
st.session_state.tareas.append({
'texto': nueva_tarea,
'completada': False
})
# Mostrar tareas existentes
if st.session_state.tareas:
st.subheader("Lista de Tareas")
for i, tarea in enumerate(st.session_state.tareas):
col1, col2 = st.columns([3, 1])
with col1:
st.write(f"• {tarea['texto']}")
with col2:
if st.button("❌", key=f"eliminar_{i}"):
st.session_state.tareas.pop(i)
st.rerun()
Gestión de estados de usuario
Una aplicación práctica común es mantener información sobre el estado de autenticación o configuración del usuario:
# Inicializar estado de autenticación
if 'usuario_logueado' not in st.session_state:
st.session_state.usuario_logueado = False
if 'nombre_usuario' not in st.session_state:
st.session_state.nombre_usuario = ""
st.title("Sistema de Login Simulado")
if not st.session_state.usuario_logueado:
# Pantalla de login
st.subheader("Iniciar Sesión")
nombre = st.text_input("Nombre de usuario:")
if st.button("Entrar") and nombre:
st.session_state.usuario_logueado = True
st.session_state.nombre_usuario = nombre
st.rerun()
else:
# Pantalla principal
st.success(f"¡Bienvenido, {st.session_state.nombre_usuario}!")
st.write("Contenido exclusivo para usuarios registrados:")
st.info("Aquí aparecería el dashboard principal de la aplicación")
if st.button("Cerrar Sesión"):
st.session_state.usuario_logueado = False
st.session_state.nombre_usuario = ""
st.rerun()
Depuración del estado de sesión
Para monitorizar y depurar el contenido de st.session_state
durante el desarrollo, puedes crear un panel de información que muestre todas las variables almacenadas:
# Panel de depuración (solo para desarrollo)
with st.expander("🔍 Debug: Contenido de session_state"):
if st.session_state:
for clave, valor in st.session_state.items():
st.write(f"**{clave}**: {valor}")
else:
st.write("session_state está vacío")
Este panel te ayuda a visualizar exactamente qué datos están siendo persistidos y cómo cambian durante la interacción del usuario.
Limitaciones importantes
Es importante tener en cuenta algunas restricciones del st.session_state
:
- Límite de memoria: Los datos se almacenan en la memoria del servidor, por lo que grandes cantidades de datos pueden afectar el rendimiento
- Duración de sesión: Los datos se pierden cuando el usuario cierra la pestaña del navegador o después de un período de inactividad
- Concurrencia: Cada usuario tiene su propio
session_state
independiente
La gestión adecuada del estado es fundamental para crear aplicaciones Streamlit interactivas y con una experiencia de usuario fluida. En la siguiente sección exploraremos cómo combinar st.session_state
con formularios para crear interfaces más robustas.
Formularios y validación de datos con st.form
Los formularios en Streamlit proporcionan una forma elegante de agrupar múltiples elementos de entrada y controlar cuándo se procesan los datos. A diferencia de los widgets individuales que se ejecutan inmediatamente al cambiar, los formularios esperan a que el usuario pulse un botón de envío antes de procesar toda la información.
Estructura básica de un formulario
La función st.form()
crea un contenedor especial donde puedes agrupar widgets de entrada. Todo el contenido dentro del formulario se procesa simultáneamente cuando el usuario pulsa el botón de envío:
import streamlit as st
st.title("Mi Primer Formulario")
with st.form("formulario_basico"):
nombre = st.text_input("Nombre completo:")
edad = st.number_input("Edad:", min_value=0, max_value=120, value=25)
# Botón de envío obligatorio
submitted = st.form_submit_button("Enviar datos")
if submitted:
st.success(f"¡Datos recibidos! Hola {nombre}, tienes {edad} años.")
El botón de envío es obligatorio en cada formulario y determina cuándo se procesan los datos introducidos por el usuario.
Combinando formularios con session_state
Los formularios trabajan de manera excelente con st.session_state
para mantener los datos enviados entre ejecuciones de la aplicación:
# Inicializar lista de usuarios
if 'usuarios_registrados' not in st.session_state:
st.session_state.usuarios_registrados = []
st.title("Registro de Usuarios")
with st.form("registro_usuario"):
st.subheader("Datos del Usuario")
nombre = st.text_input("Nombre:")
email = st.text_input("Email:")
edad = st.number_input("Edad:", min_value=18, max_value=100, value=25)
submitted = st.form_submit_button("Registrar Usuario")
if submitted and nombre and email:
# Añadir usuario a la lista persistente
nuevo_usuario = {
'nombre': nombre,
'email': email,
'edad': edad
}
st.session_state.usuarios_registrados.append(nuevo_usuario)
st.success(f"Usuario {nombre} registrado correctamente!")
# Mostrar usuarios registrados
if st.session_state.usuarios_registrados:
st.subheader("Usuarios Registrados")
for usuario in st.session_state.usuarios_registrados:
st.info(f"**{usuario['nombre']}** - {usuario['email']} ({usuario['edad']} años)")
Validación básica de datos
La validación es esencial para garantizar que los datos introducidos cumplan con los requisitos específicos. Puedes implementar validaciones simples directamente en el formulario:
st.title("Formulario con Validación")
with st.form("formulario_validado"):
# Campos del formulario
nombre = st.text_input("Nombre completo:")
telefono = st.text_input("Teléfono (formato: 123456789):")
email = st.text_input("Email:")
password = st.text_input("Contraseña:", type="password")
submitted = st.form_submit_button("Validar y Enviar")
if submitted:
# Lista para almacenar errores
errores = []
# Validar nombre
if not nombre or len(nombre) < 2:
errores.append("El nombre debe tener al menos 2 caracteres")
# Validar teléfono
if not telefono.isdigit() or len(telefono) != 9:
errores.append("El teléfono debe tener exactamente 9 dígitos")
# Validar email
if "@" not in email or "." not in email.split("@")[-1]:
errores.append("El email no tiene un formato válido")
# Validar contraseña
if len(password) < 6:
errores.append("La contraseña debe tener al menos 6 caracteres")
# Mostrar resultados
if errores:
for error in errores:
st.error(f"❌ {error}")
else:
st.success("✅ Todos los datos son válidos!")
st.balloons()
Formulario de configuración avanzado
Los formularios son ideales para páginas de configuración donde el usuario necesita ajustar múltiples parámetros a la vez:
# Inicializar configuración por defecto
if 'config_app' not in st.session_state:
st.session_state.config_app = {
'tema': 'Claro',
'idioma': 'Español',
'notificaciones': True,
'max_elementos': 10
}
st.title("Configuración de la Aplicación")
with st.form("configuracion"):
st.subheader("Preferencias Generales")
# Opciones de tema
tema = st.selectbox(
"Tema visual:",
options=['Claro', 'Oscuro', 'Automático'],
index=['Claro', 'Oscuro', 'Automático'].index(st.session_state.config_app['tema'])
)
# Idioma
idioma = st.radio(
"Idioma:",
options=['Español', 'Inglés', 'Francés'],
index=['Español', 'Inglés', 'Francés'].index(st.session_state.config_app['idioma'])
)
# Notificaciones
notificaciones = st.checkbox(
"Activar notificaciones",
value=st.session_state.config_app['notificaciones']
)
# Número máximo de elementos
max_elementos = st.slider(
"Máximo de elementos por página:",
min_value=5, max_value=50,
value=st.session_state.config_app['max_elementos']
)
# Botones del formulario
col1, col2 = st.columns(2)
with col1:
guardar = st.form_submit_button("💾 Guardar Configuración")
with col2:
restablecer = st.form_submit_button("🔄 Restablecer")
if guardar:
# Actualizar configuración
st.session_state.config_app = {
'tema': tema,
'idioma': idioma,
'notificaciones': notificaciones,
'max_elementos': max_elementos
}
st.success("Configuración guardada correctamente!")
if restablecer:
# Volver a valores por defecto
st.session_state.config_app = {
'tema': 'Claro',
'idioma': 'Español',
'notificaciones': True,
'max_elementos': 10
}
st.info("Configuración restablecida a valores por defecto")
st.rerun()
# Mostrar configuración actual
st.subheader("Configuración Actual")
config_actual = st.session_state.config_app
st.json(config_actual)
Formularios con validación condicional
Para casos más complejos, puedes implementar validaciones que dependen de los valores de otros campos:
st.title("Solicitud de Cuenta Bancaria")
with st.form("solicitud_cuenta"):
# Datos personales
st.subheader("Información Personal")
nombre = st.text_input("Nombre completo:")
edad = st.number_input("Edad:", min_value=16, max_value=100, value=25)
# Información financiera
st.subheader("Información Financiera")
ingresos = st.number_input("Ingresos mensuales (€):", min_value=0.0, value=1000.0)
tipo_cuenta = st.selectbox("Tipo de cuenta:", ["Básica", "Premium", "VIP"])
# Documentación
st.subheader("Documentación")
tiene_dni = st.checkbox("Tengo DNI válido")
tiene_nomina = st.checkbox("Tengo nómina")
submitted = st.form_submit_button("Solicitar Cuenta")
if submitted:
errores = []
advertencias = []
# Validaciones básicas
if not nombre:
errores.append("El nombre es obligatorio")
# Validaciones condicionales según edad
if edad < 18:
errores.append("Debes ser mayor de edad para abrir una cuenta")
elif edad < 25:
advertencias.append("Los menores de 25 años requieren avalista")
# Validaciones según tipo de cuenta
if tipo_cuenta == "Premium" and ingresos < 2000:
errores.append("La cuenta Premium requiere ingresos mínimos de 2000€")
elif tipo_cuenta == "VIP" and ingresos < 5000:
errores.append("La cuenta VIP requiere ingresos mínimos de 5000€")
# Validar documentación
if not tiene_dni:
errores.append("El DNI es obligatorio")
if tipo_cuenta != "Básica" and not tiene_nomina:
errores.append("Las cuentas Premium y VIP requieren nómina")
# Mostrar resultados
if errores:
st.subheader("❌ Errores encontrados:")
for error in errores:
st.error(error)
else:
if advertencias:
st.subheader("⚠️ Advertencias:")
for advertencia in advertencias:
st.warning(advertencia)
st.success(f"✅ Solicitud de cuenta {tipo_cuenta} aprobada para {nombre}")
st.info(f"Procesaremos tu solicitud en 2-3 días laborables")
Formularios anidados y organizados
Para interfaces complejas, puedes organizar formularios usando columnas y expandir secciones para mejorar la experiencia del usuario:
st.title("Formulario de Contacto Empresarial")
with st.form("contacto_empresarial"):
# Dividir en columnas para mejor organización
col1, col2 = st.columns(2)
with col1:
st.subheader("Datos de Contacto")
nombre = st.text_input("Nombre:")
empresa = st.text_input("Empresa:")
email = st.text_input("Email corporativo:")
with col2:
st.subheader("Información Adicional")
telefono = st.text_input("Teléfono:")
cargo = st.text_input("Cargo:")
sector = st.selectbox("Sector:", [
"Tecnología", "Finanzas", "Salud",
"Educación", "Retail", "Otro"
])
# Sección expandible para más detalles
with st.expander("Detalles de la Consulta (Opcional)"):
tipo_consulta = st.multiselect(
"Tipo de consulta:",
["Soporte técnico", "Ventas", "Facturación", "Partnerships"]
)
mensaje = st.text_area("Mensaje detallado:", height=100)
urgencia = st.select_slider(
"Nivel de urgencia:",
options=["Baja", "Media", "Alta", "Crítica"]
)
# Términos y condiciones
acepta_terminos = st.checkbox("Acepto los términos y condiciones")
acepta_marketing = st.checkbox("Acepto recibir comunicaciones comerciales")
enviado = st.form_submit_button("📨 Enviar Consulta")
if enviado:
if not acepta_terminos:
st.error("Debes aceptar los términos y condiciones")
elif not all([nombre, empresa, email]):
st.error("Los campos básicos son obligatorios")
else:
# Simular envío exitoso
st.success("¡Consulta enviada correctamente!")
# Mostrar resumen
st.subheader("Resumen de tu consulta:")
st.write(f"**Contacto**: {nombre} ({cargo}) - {empresa}")
st.write(f"**Email**: {email}")
if tipo_consulta:
st.write(f"**Tipo**: {', '.join(tipo_consulta)}")
if mensaje:
st.write(f"**Mensaje**: {mensaje}")
Los formularios con st.form()
ofrecen un control preciso sobre cuándo y cómo se procesan los datos del usuario. La combinación de formularios, validación y st.session_state
permite crear interfaces robustas que proporcionan feedback inmediato y mantienen el estado de la aplicación de manera coherente.
Fuentes y referencias
Documentación oficial y recursos externos para profundizar en Streamlit
Documentación oficial de Streamlit
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, Streamlit 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 Streamlit
Explora más contenido relacionado con Streamlit y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
- Comprender el uso de st.session_state para mantener datos persistentes durante la sesión del usuario.
- Aprender a inicializar y manipular variables dentro de st.session_state.
- Implementar formularios con st.form para agrupar entradas y controlar el envío de datos.
- Aplicar validaciones básicas y condicionales en formularios para asegurar la integridad de los datos.
- Combinar la gestión de estado con formularios para crear aplicaciones interactivas y robustas.