Saltar al contenido
🐍 Curso de Python
/

Módulo 4 · Lección 35 de 45

Mensajes y sesiones

El aviso de que algo ha ido bien, que sobrevive a una redirección, y la lista de la sesión que se niega a crecer.

⏱ 14 min de lectura

Desde la lección 24 hay un agujero apuntado y sin tapar: guardas un contacto, la aplicación redirige, y no hay ninguna señal de que haya ido bien. Ese es el precio del patrón de la lección 2, porque después de redirigir la vista original ya no existe y su contexto tampoco.

Django trae la pieza para eso, y está construida sobre la que también permite guardar cualquier cosa entre peticiones. Las dos van juntas en esta lección porque una usa la otra.

Mensajes#

Ya estaba todo puesto desde la lección 4: la aplicación en INSTALLED_APPS, el componente en MIDDLEWARE y el procesador de contexto en TEMPLATES. Solo hay que usarlo.

contactos/views.py
from django.contrib import messages


def nuevo(peticion):
        if formulario.is_valid():
                formulario.save()
        messages.success(peticion, 'Contacto guardado.')
        return redirect('contactos:lista')

Y en la plantilla base, para que salga en cualquier página. Ese es el bloque mensajes que la lección 18 recomendó dejar previsto:

plantillas/base.html
{% block mensajes %}
  {% if messages %}
  <ul class="mensajes">
    {% for m in messages %}
      <li class="{{ m.tags }}">{{ m }}</li>
    {% endfor %}
  </ul>
  {% endif %}
{% endblock %}
Salida en pantalla
[25|success|success] Contacto guardado.
[30|warning|warning] Le falta el teléfono.

Dos cosas que hacen que esto funcione. La variable messages está en todas las plantillas sin que ninguna vista la pase, por el procesador de contexto de la lección 33. Y m.tags es la clase CSS del nivel, lo que permite pintar los verdes de los rojos con dos reglas.

Se leen una vez#

Salida en pantalla
después de la redirección  ->  los dos mensajes
al recargar la página      ->  SIN MENSAJES

Un mensaje se consume al leerlo. Es lo que se quiere: el aviso de "guardado" no debe seguir ahí al recargar.

Y tiene una consecuencia que desconcierta: si una plantilla recorre messages en un sitio y otra parte de la misma página vuelve a recorrerlo, la segunda vez está vacío. Cuando haga falta usarlos dos veces, se guardan en una variable con el {% with %} de la lección 17.

Los cinco niveles#

Salida en pantalla
messages.DEBUG    = 10
messages.INFO     = 20
messages.SUCCESS  = 25
messages.WARNING  = 30
messages.ERROR    = 40

etiquetas: {10: 'debug', 20: 'info', 25: 'success', 30: 'warning', 40: 'error'}

En la medición de arriba se mandaron tres mensajes y solo salieron dos: el de debug no aparece. No es un fallo, es que MESSAGE_LEVEL vale INFO, o sea 20, y lo que esté por debajo se descarta.

Eso se cambia en los ajustes, y es útil en desarrollo:

agenda/settings.py
MESSAGE_LEVEL = 10   # que se vean también los de depuración

Las cinco funciones son messages.debug, info, success, warning y error. Y con messages.error se puede avisar de un problema sin que sea un error del formulario, que es justo lo que faltaba en algunos casos de la lección 27.

Sesiones#

Un diccionario que sobrevive entre peticiones de la misma persona:

python
def cuenta_visitas(peticion):
        peticion.session['visitas'] = peticion.session.get('visitas', 0) + 1
    return HttpResponse(f"visitas={peticion.session['visitas']}")
Salida en pantalla
visitas=1
visitas=2
visitas=3

Se usa como un diccionario normal: corchetes, get, pop, del, in. Lo que se guarda tiene que poder serializarse a JSON, así que valen textos, números, listas y diccionarios, y no valen objetos de modelo. Lo que se guarda de un objeto es su identificador.

Dónde viven y cuánto duran#

Salida en pantalla
SESSION_ENGINE:                  django.contrib.sessions.backends.db
SESSION_COOKIE_AGE:              1209600  (14 días)
SESSION_EXPIRE_AT_BROWSER_CLOSE: False
SESSION_SAVE_EVERY_REQUEST:      False
SESSION_COOKIE_HTTPONLY:         True
SESSION_COOKIE_SECURE:           False
SESSION_COOKIE_SAMESITE:         Lax

De serie, en una tabla:

Salida en pantalla
session_key:  59b9dgmebihrf6...
session_data: .eJxVjEEOwiAQRe_C2hBgWkCX7nsGMgORVA0kpV0Z765NutDtf...
decodificado: {'_auth_user_id': '2', '_auth_user_backend': '...ModelBackend',
               '_auth_user_hash': '3b0a11cf482a...'}
expire_date:  2026-10-06 17:31:59+00:00

En la cookie solo va la clave; los datos están en el servidor. Ahí se ve también cómo se guarda la sesión iniciada de la lección 32: tres entradas con el identificador del usuario, el sistema que lo autenticó y un resumen de su contraseña, que es lo que hace que cambiar la contraseña cierre las demás sesiones.

Los otros motores, por si el de serie no encaja:

SESSION_ENGINECuándo
backends.dbLo de serie. Una consulta por petición
backends.cacheRápido; se pierde todo si se reinicia la caché
backends.cached_dbLee de la caché y escribe en las dos. Lo habitual con tráfico
backends.signed_cookiesSin servidor; los datos viajan en la cookie, firmados pero legibles

El último engaña: firmar no es cifrar. Cualquiera puede leer lo que hay dentro, así que no sirve para nada que no deba verse.

Y una tarea de mantenimiento que se olvida siempre:

consola
python manage.py clearsessions

Django no borra las sesiones caducadas solo. Con el motor de base de datos, esa tabla crece hasta hacerse enorme, y esa orden hay que programarla.