Flask

Flask

Tutorial Flask: Flash messages

Aprende a usar mensajes flash en Flask para mostrar feedback temporal con categorías y estilos, mejorando la experiencia de usuario en tus aplicaciones web.

Aprende Flask y certifícate

Sistema de mensajes flash

Los mensajes flash en Flask proporcionan una forma elegante de mostrar información temporal al usuario después de realizar una acción específica. Este sistema permite almacenar mensajes en la sesión del usuario y mostrarlos en la siguiente petición, siendo especialmente útil para proporcionar feedback inmediato tras el envío de formularios.

Flask incluye de forma nativa la función flash() que permite almacenar mensajes temporales. Estos mensajes se mantienen disponibles únicamente para la próxima petición, después de la cual se eliminan automáticamente. Esta característica los convierte en la solución ideal para notificar al usuario sobre el resultado de operaciones como crear, actualizar o eliminar registros.

Configuración básica de mensajes flash

Para utilizar mensajes flash, tu aplicación Flask debe tener configurada una clave secreta. Esta clave es necesaria porque Flask utiliza las sesiones para almacenar temporalmente los mensajes:

from flask import Flask

app = Flask(__name__)
app.secret_key = 'tu_clave_secreta_aqui'

Una vez configurada la clave secreta, puedes usar la función flash() en cualquier ruta de tu aplicación. La función acepta el mensaje como primer parámetro:

from flask import flash, redirect, url_for

@app.route('/crear-usuario', methods=['POST'])
def crear_usuario():
    # Lógica para crear usuario
    usuario_creado = True
    
    if usuario_creado:
        flash('Usuario creado exitosamente')
        return redirect(url_for('lista_usuarios'))
    else:
        flash('Error al crear el usuario')
        return redirect(url_for('formulario_usuario'))

Mostrar mensajes en plantillas Jinja

Para mostrar los mensajes flash en tus plantillas, Flask proporciona la función get_flashed_messages() que está disponible automáticamente en el contexto de Jinja. Esta función devuelve una lista con todos los mensajes flash pendientes:

<!-- En tu plantilla base.html -->
{% with messages = get_flashed_messages() %}
    {% if messages %}
        <div class="flash-messages">
            {% for message in messages %}
                <div class="alert">
                    {{ message }}
                </div>
            {% endfor %}
        </div>
    {% endif %}
{% endwith %}

También puedes usar una sintaxis más directa si prefieres iterar directamente sobre los mensajes:

{% for message in get_flashed_messages() %}
    <div class="notification">
        <p>{{ message }}</p>
    </div>
{% endfor %}

Integración con formularios WTForms

Los mensajes flash se integran perfectamente con WTForms para proporcionar feedback después de la validación y procesamiento de formularios. Aquí tienes un ejemplo práctico:

from flask import render_template, flash, redirect, url_for
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired

class ProductoForm(FlaskForm):
    nombre = StringField('Nombre del producto', validators=[DataRequired()])
    submit = SubmitField('Guardar')

@app.route('/productos/nuevo', methods=['GET', 'POST'])
def nuevo_producto():
    form = ProductoForm()
    
    if form.validate_on_submit():
        # Procesar datos del formulario
        nombre_producto = form.nombre.data
        
        # Simular guardado en base de datos
        producto_guardado = guardar_producto(nombre_producto)
        
        if producto_guardado:
            flash(f'Producto "{nombre_producto}" creado correctamente')
            return redirect(url_for('lista_productos'))
        else:
            flash('Error al guardar el producto. Inténtalo de nuevo')
    
    return render_template('nuevo_producto.html', form=form)

En la plantilla correspondiente, puedes mostrar tanto los mensajes flash como los errores de validación del formulario:

<!-- nuevo_producto.html -->
{% extends "base.html" %}

{% block content %}
    <!-- Mostrar mensajes flash -->
    {% for message in get_flashed_messages() %}
        <div class="alert alert-info">
            {{ message }}
        </div>
    {% endfor %}
    
    <form method="POST">
        {{ form.hidden_tag() }}
        
        <div class="form-group">
            {{ form.nombre.label(class="form-label") }}
            {{ form.nombre(class="form-control") }}
            
            <!-- Mostrar errores de validación -->
            {% if form.nombre.errors %}
                {% for error in form.nombre.errors %}
                    <div class="text-danger">{{ error }}</div>
                {% endfor %}
            {% endif %}
        </div>
        
        {{ form.submit(class="btn btn-primary") }}
    </form>
{% endblock %}

Patrón POST-Redirect-GET

Los mensajes flash funcionan especialmente bien con el patrón POST-Redirect-GET (PRG), que es una práctica recomendada en aplicaciones web. Este patrón evita que los usuarios reenvíen accidentalmente formularios al actualizar la página:

@app.route('/editar-producto/<int:id>', methods=['GET', 'POST'])
def editar_producto(id):
    producto = obtener_producto(id)
    form = ProductoForm(obj=producto)
    
    if form.validate_on_submit():
        # Actualizar producto con datos del formulario
        producto.nombre = form.nombre.data
        
        if actualizar_producto(producto):
            flash('Producto actualizado correctamente')
            # Redirección después del POST exitoso
            return redirect(url_for('detalle_producto', id=producto.id))
        else:
            flash('Error al actualizar el producto')
    
    # Mostrar formulario en peticiones GET o POST inválidas
    return render_template('editar_producto.html', form=form, producto=producto)

Este enfoque garantiza que los mensajes flash se muestren correctamente después de operaciones exitosas, mientras que los errores de validación se mantienen en la misma página para que el usuario pueda corregirlos inmediatamente.

Categorías de mensajes

Flask permite organizar los mensajes flash mediante categorías, proporcionando una forma de clasificar los mensajes según su propósito o nivel de importancia. Esta funcionalidad resulta especialmente útil para aplicar diferentes estilos visuales y comportamientos según el tipo de mensaje que se desea mostrar al usuario.

Definición de categorías en mensajes flash

La función flash() acepta un segundo parámetro opcional que especifica la categoría del mensaje. Si no se proporciona ninguna categoría, Flask asigna automáticamente la categoría 'message' por defecto:

from flask import flash

# Mensaje sin categoría (categoría 'message' por defecto)
flash('Operación completada')

# Mensajes con categorías específicas
flash('Usuario creado exitosamente', 'success')
flash('Campos obligatorios sin completar', 'error')
flash('Los cambios se guardarán automáticamente', 'info')
flash('Esta acción no se puede deshacer', 'warning')

Las categorías más comunes en aplicaciones web incluyen success, error, warning e info, aunque puedes definir cualquier nombre de categoría que se adapte a las necesidades de tu aplicación.

Recuperación de mensajes por categoría

Para trabajar con mensajes categorizados en las plantillas, la función get_flashed_messages() ofrece varios parámetros que permiten filtrar y organizar los mensajes:

# En el controlador, enviamos mensajes de diferentes categorías
@app.route('/procesar-pedido', methods=['POST'])
def procesar_pedido():
    form = PedidoForm()
    
    if form.validate_on_submit():
        try:
            pedido = crear_pedido(form.data)
            flash('Pedido creado correctamente', 'success')
            flash('Recibirás un email de confirmación', 'info')
        except StockInsuficienteError:
            flash('Algunos productos no tienen stock suficiente', 'warning')
        except Exception:
            flash('Error al procesar el pedido', 'error')
    
    return redirect(url_for('resumen_pedido'))

En las plantillas Jinja, puedes recuperar mensajes específicos por categoría utilizando el parámetro category_filter:

<!-- Mostrar solo mensajes de error -->
{% for message in get_flashed_messages(category_filter=['error']) %}
    <div class="alert alert-danger">
        <strong>Error:</strong> {{ message }}
    </div>
{% endfor %}

<!-- Mostrar mensajes de éxito e información -->
{% for message in get_flashed_messages(category_filter=['success', 'info']) %}
    <div class="alert alert-success">
        {{ message }}
    </div>
{% endfor %}

Acceso a categorías junto con mensajes

Para obtener tanto el mensaje como su categoría, utiliza el parámetro with_categories=True. Este parámetro hace que get_flashed_messages() devuelva una lista de tuplas donde cada tupla contiene la categoría y el mensaje:

{% for category, message in get_flashed_messages(with_categories=true) %}
    <div class="alert alert-{{ category }}">
        {% if category == 'error' %}
            <i class="icon-error"></i>
        {% elif category == 'success' %}
            <i class="icon-check"></i>
        {% elif category == 'warning' %}
            <i class="icon-warning"></i>
        {% else %}
            <i class="icon-info"></i>
        {% endif %}
        {{ message }}
    </div>
{% endfor %}

Implementación práctica con estilos CSS

Una implementación común consiste en mapear las categorías de mensajes con clases CSS específicas para crear una experiencia visual coherente:

<!-- En tu plantilla base -->
{% with messages = get_flashed_messages(with_categories=true) %}
    {% if messages %}
        <div class="flash-container">
            {% for category, message in messages %}
                <div class="flash-message flash-{{ category }}">
                    <span class="flash-icon">
                        {% if category == 'success' %}✓{% endif %}
                        {% if category == 'error' %}✗{% endif %}
                        {% if category == 'warning' %}⚠{% endif %}
                        {% if category == 'info' %}ℹ{% endif %}
                    </span>
                    <span class="flash-text">{{ message }}</span>
                </div>
            {% endfor %}
        </div>
    {% endif %}
{% endwith %}

El CSS correspondiente podría definir estilos específicos para cada categoría:

.flash-message {
    padding: 12px 16px;
    margin: 8px 0;
    border-radius: 4px;
    display: flex;
    align-items: center;
}

.flash-success {
    background-color: #d4edda;
    color: #155724;
    border: 1px solid #c3e6cb;
}

.flash-error {
    background-color: #f8d7da;
    color: #721c24;
    border: 1px solid #f5c6cb;
}

.flash-warning {
    background-color: #fff3cd;
    color: #856404;
    border: 1px solid #ffeaa7;
}

.flash-info {
    background-color: #d1ecf1;
    color: #0c5460;
    border: 1px solid #bee5eb;
}

Ejemplo completo con formulario WTForms

Aquí tienes un ejemplo que integra categorías de mensajes con la validación de formularios:

@app.route('/configuracion/perfil', methods=['GET', 'POST'])
def actualizar_perfil():
    form = PerfilForm()
    
    if form.validate_on_submit():
        try:
            usuario = obtener_usuario_actual()
            
            # Verificar si el email ya existe
            if form.email.data != usuario.email:
                if email_existe(form.email.data):
                    flash('El email ya está en uso por otro usuario', 'warning')
                    return render_template('perfil.html', form=form)
            
            # Actualizar datos
            usuario.nombre = form.nombre.data
            usuario.email = form.email.data
            
            guardar_usuario(usuario)
            
            flash('Perfil actualizado correctamente', 'success')
            flash('Los cambios pueden tardar unos minutos en aplicarse', 'info')
            
            return redirect(url_for('ver_perfil'))
            
        except ValidationError as e:
            flash(f'Error de validación: {str(e)}', 'error')
        except Exception:
            flash('Error interno del servidor. Inténtalo más tarde', 'error')
    
    return render_template('perfil.html', form=form)

Esta aproximación permite crear una experiencia de usuario más rica y informativa, donde cada tipo de mensaje transmite claramente su propósito y urgencia mediante el uso apropiado de categorías y estilos visuales diferenciados.

Aprende Flask online

Otras lecciones de Flask

Accede a todas las lecciones de Flask y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a Flask y certifícate

Ejercicios de programación de Flask

Evalúa tus conocimientos de esta lección Flash messages con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.