Django Sessions: qué es request.session, cómo usarlo y mejores prácticas

- Andrés Cruz - EN In english

Video thumbnail

HTTP (Protocolo de Transferencia de Hipertexto) es el protocolo fundamental de la web. Por diseño, es un protocolo sin estado: cada solicitud que hace un navegador al servidor se procesa de forma completamente independiente, sin ninguna memoria de las interacciones anteriores. Una vez que el servidor responde, olvida quién hizo la petición.

Este comportamiento plantea un problema inmediato: ¿cómo sabe una aplicación web que el usuario que navega por la página de perfil es el mismo que inició sesión hace unos minutos? Ahí es donde entra en juego el sistema de sesiones de Django.

En el artículo anterior vimos cómo manejar archivos estáticos en Django. Ahora vamos con uno de los conceptos más importantes para cualquier aplicación web real: la sesión.

Qué es una sesión en Django y por qué es necesaria

Una sesión es un mecanismo que permite asociar datos a un usuario a lo largo de múltiples solicitudes HTTP. En otras palabras, es la forma en que Django "recuerda" quién eres entre una petición y la siguiente.

El proceso funciona así: cuando un usuario visita la aplicación por primera vez, Django crea una nueva sesión y envía al navegador una cookie con un identificador único llamado sessionid. Ese identificador actúa como un puente entre el cliente y el servidor: en cada solicitud posterior, el navegador envía automáticamente el sessionid en la cabecera Cookie, y Django lo usa para recuperar los datos de sesión almacenados previamente en el servidor.

Es importante entender que la cookie solo contiene ese identificador; los datos reales (nombre de usuario, carrito de compra, preferencias, etc.) se almacenan en el servidor. Esto es lo que distingue a las sesiones de las cookies simples y lo que las hace más seguras.

La responsable de todo este proceso es la clase SessionMiddleware, que intercepta cada solicitud entrante para leer el sessionid y hace que los datos de sesión estén disponibles en request.session durante el procesamiento de la vista. Al finalizar, guarda los cambios antes de enviar la respuesta al cliente.

Cómo Django gestiona las sesiones internamente

El flujo interno del SessionMiddleware en cada ciclo solicitud-respuesta es el siguiente:

  • Lee el sessionid de la cookie del cliente (si existe).
  • Recupera los datos asociados desde el motor de sesión configurado (base de datos, archivo, caché, etc.).
  • Los expone en el objeto request.session, accesible desde cualquier vista.
  • Al terminar la vista, guarda los cambios y actualiza la cookie si es necesario antes de enviar la respuesta.

Este flujo es el "pegamento" invisible que mantiene la coherencia entre páginas, formularios y usuarios a lo largo de toda la navegación. Es, de hecho, el mecanismo base sobre el que Django construye su sistema de autenticación: sin sesiones, no habría usuarios autenticados.

Configuración básica del middleware de sesión en settings.py

El SessionMiddleware viene activado por defecto en la mayoría de proyectos Django, pero siempre conviene verificar que esté presente en MIDDLEWARE y en el orden correcto. Es importante que aparezca antes de AuthenticationMiddleware, ya que el sistema de autenticación de Django depende de que las sesiones ya estén disponibles:

MIDDLEWARE = [
    ...
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    ...
]

Después de ejecutar python manage.py migrate, Django crea automáticamente la tabla django_session en la base de datos, que es donde se almacenarán los datos de sesión por defecto.

El objeto request.session funciona exactamente igual que un diccionario de Python: puedes almacenar, leer y eliminar datos mediante pares clave-valor. Cualquier tipo de dato serializable con JSON (números, strings, listas, diccionarios) puede guardarse en él.

Usar request.session en una vista Django

Un ejemplo clásico para entender las sesiones es un contador de visitas: queremos contar cuántas veces un mismo usuario (o más exactamente, el mismo navegador) ha accedido a una URL. El decorador @never_cache es importante: le indica al navegador y a proxies intermedios que no cacheen la respuesta, garantizando que el contador siempre se lea y actualice desde la sesión real.

# views.py (Variante 1: Uso de render y acceso directo a la sesión)

from django.shortcuts import render
from django.views.decorators.cache import never_cache

# NOTA: En este caso, 'render' necesita un template de archivo (por ejemplo, 'visits.html')
# Asumiendo que existe un archivo 'visits.html' con el contenido: <h1>Total visits: {{ n_visits }}</h1>

@never_cache
def sessionvisit_view_variant1(request):
    # 1. Obtener y actualizar el contador
    current_visits = request.session.get('n_visits', 0)
    new_visits = current_visits + 1
    
    # 2. Guardar el nuevo valor en la sesión
    request.session['n_visits'] = new_visits
    
    # 3. Usar el atajo 'render' para cargar y responder. 
    # Aquí pasamos el *nuevo* valor para que se muestre correctamente.
    return render(request, 'visits.html', {'n_visits': new_visits})

Cada vez que actualizo la página, el recuento aumenta. El segundo argumento de request.session.get('n_visits', 0) actúa como valor por defecto: si la clave no existe aún en la sesión, retorna 0 en lugar de lanzar un error.

En la tabla django_session, puedo encontrar las claves de sesión y los datos.

También podemos visualizar los atributos de la sesión en el navegador usando las herramientas de desarrollo (cabecera Set-Cookie). Así, el navegador almacena el sessionid en una cookie y cada vez que realiza una solicitud al servidor, lo transmite dentro del encabezado Cookie.

El campo session_data que verás en la base de datos es una cadena codificada en base64. Podemos decodificarlo así:

import base64
import json

session_str = "eyJuX3Zpc2l0cyI6MTR9:1rQVYV:tvm4LNPRtDXalmCcaR3cXeIMgjj3c8BqzwDca0GdK0U"

# Split the string to get the base64 portion
base64_data = session_str.split(":")[0]

# Pad the string if necessary
padded_base64_data = base64_data + '=' * (-len(base64_data) % 4)

# Decode the base64 string
decoded_data = base64.b64decode(padded_base64_data)

# Assuming the decoded data is JSON serialized
deserialized_data = json.loads(decoded_data)

print(deserialized_data)

"""
{'n_visits': 14}
"""

Las sesiones y las cookies son componentes clave en la gestión de datos de estado y de usuario en aplicaciones web, pero tienen diferentes propósitos y operan de distintas maneras.

Diferencias clave entre sesiones y cookies en Django

Ubicación de almacenamiento

  • Cookies: Los datos se almacenan en el navegador del usuario (lado del cliente).
  • Sesiones: Los datos se almacenan en el servidor. El navegador solo guarda el sessionid, que es una referencia a esos datos.

Seguridad

  • Cookies: Al vivir en el cliente, son más vulnerables a manipulación (tampering) y robo de datos mediante ataques XSS. Nunca deben contener información sensible en texto plano.
  • Sesiones: Más seguras por diseño, ya que el cliente solo conoce el sessionid y nunca los datos reales. Aun así, proteger ese identificador es crítico (de ahí la importancia de usar HTTPS).

Tamaño y capacidad

  • Cookies: Limitadas a unos 4 KB por cookie. No son aptas para grandes volúmenes de datos.
  • Sesiones: El almacenamiento está limitado solo por el motor que uses (base de datos, sistema de archivos, Redis, etc.), por lo que pueden manejar cantidades de datos mucho mayores.

Casos de uso

  • Cookies: Ideales para guardar preferencias del usuario (tema oscuro/claro, idioma) o datos de seguimiento no sensibles que deben persistir entre sesiones de navegación.
  • Sesiones: La opción correcta para gestionar la autenticación, manejar carritos de compra temporales o almacenar cualquier dato sensible que solo debe durar mientras el usuario navega.

Cómo guardar, leer y eliminar datos en la Sesión

Trabajar con request.session es tan sencillo como manejar un diccionario de Python. A continuación, los tres patrones que usarás constantemente.

1. Guardar un valor

Para guardar información en sesión, simplemente asigna un valor a una clave. Por ejemplo, guardamos el ID de un comentario recién creado para mostrarlo en la siguiente página:

def add_comment(request):
   # ... lógica de creación del comentario ...
   request.session['comment_id'] = comment.id
   return redirect('index')

2. Consultar un valor de forma segura

Como request.session es accesible en cualquier vista, puedes consultar ese dato desde cualquier punto de la aplicación. Sin embargo, si la clave no existe, Django lanzará un KeyError. La forma segura de leerla es comprobar primero si existe, o usar el método .get() con un valor por defecto:

def index(request):
   context = {}
   if 'comment_id' in request.session:
       # Convertimos a string para evitar errores al concatenar con texto
       context['last_comment'] = "Último comentario ID: " + str(request.session['comment_id'])
   return render(request, 'index.html', context)

3. Destruir un valor específico

Si queremos que el mensaje del último comentario aparezca solo una vez y luego desaparezca (el patrón conocido como flash message), hacemos lo siguiente:

def index(request):
   if 'comment_id' in request.session:
       print(f"Mostrando ID: {request.session['comment_id']}")
       # Eliminamos la clave de la sesión
       del request.session['comment_id']
   
   return render(request, 'index.html')

También puedes usar request.session.pop('comment_id', None) como alternativa más concisa: elimina y retorna el valor en una sola operación sin lanzar error si la clave no existe.

Limitaciones: ¿Qué podemos guardar?

Puedes guardar estructuras simples como números, strings o listas (arrays):

request.session['comments'] = [1,2,3,4,5]

Sin embargo, no puedes guardar objetos complejos directamente (como una instancia de un modelo de base de datos) porque la sesión no sabe cómo serializar objetos de Django por defecto.

  • ❌ Lo incorrecto: request.session['user_obj'] = User.objects.get(id=1)
  • ✅ Lo correcto: Guarda simplemente la PK (ID) del objeto y luego, cuando necesites el objeto, búscalo en la base de datos usando ese ID almacenado en la sesión: request.session['user_id'] = user.pk

Motores de almacenamiento de sesión: SESSION_ENGINE

Por defecto, Django almacena los datos de sesión en la base de datos mediante django.contrib.sessions.backends.db. Puedes cambiar este comportamiento ajustando SESSION_ENGINE en tu settings.py:

  • django.contrib.sessions.backends.db — Base de datos (por defecto). Simple y confiable.
  • django.contrib.sessions.backends.cache — Caché (Redis, Memcached). Mucho más rápido, ideal para alto tráfico.
  • django.contrib.sessions.backends.cached_db — Combinación: lee de caché y escribe en base de datos como respaldo.
  • django.contrib.sessions.backends.file — Sistema de archivos. Útil en desarrollo local.

Para proyectos en producción con alto tráfico, usar Redis como backend de sesión (junto con django-redis) es una práctica muy extendida que reduce considerablemente la carga en la base de datos y mejora los tiempos de respuesta.

Buenas prácticas y seguridad en sesiones Django

Las sesiones gestionan datos sensibles del usuario, por lo que aplicar buenas prácticas de seguridad es indispensable. Te recomiendo:

  • Habilitar HTTPS y usar SESSION_COOKIE_SECURE = True en producción, para que la cookie de sesión solo se transmita por conexiones cifradas.
  • Activar SESSION_COOKIE_HTTPONLY = True (ya habilitado por defecto en Django) para impedir que JavaScript del cliente acceda a la cookie, mitigando ataques XSS.
  • No almacenar información sensible (contraseñas, tokens de API, datos de tarjetas) directamente en la sesión.
  • Usar SESSION_EXPIRE_AT_BROWSER_CLOSE = True si deseas que la sesión expire automáticamente cuando el usuario cierre el navegador.
  • Definir un tiempo de expiración razonable con SESSION_COOKIE_AGE (en segundos). El valor por defecto son dos semanas (1209600 segundos).
  • Eliminar sesiones expiradas regularmente con el comando de gestión de Django:
$ python manage.py clearsessions

Este comando elimina de la base de datos todas las sesiones cuya fecha de expiración ya ha pasado. Se recomienda programarlo como una tarea periódica (por ejemplo, con cron o con django-celery-beat) para mantener la tabla django_session limpia y evitar que crezca indefinidamente.

En entornos de alto tráfico, también puede ser más eficiente usar cache-based sessions (django.contrib.sessions.backends.cache).

Preguntas frecuentes sobre sesiones en Django

  • ¿Dónde se almacenan los datos de sesión por defecto?
    • En la tabla django_session de la base de datos. Puedes cambiar el backend configurando SESSION_ENGINE en settings.py.
  • ¿Cómo borrar una sesión o cerrar sesión del usuario?
    • Usa request.session.flush(): elimina todos los datos de la sesión actual y genera un nuevo sessionid, invalidando la sesión anterior por completo.
  • ¿Cómo inspeccionar el contenido de request.session?
    • Basta con imprimirlo en consola o en una vista:
    • print(dict(request.session.items()))
  • ¿Por qué Django Admin requiere SessionMiddleware?
    • Porque el panel de administración usa el sistema de autenticación de Django, que depende de las sesiones para mantener al usuario conectado entre peticiones. Sin SessionMiddleware, el AuthenticationMiddleware no puede asociar usuarios a las solicitudes correctamente.
  • ¿Cuál es el nombre por defecto de la cookie de sesión (SESSION_COOKIE_NAME)?
    • El nombre por defecto es sessionid. Puedes cambiarlo con la variable SESSION_COOKIE_NAME en tu settings.py.
  • ¿Cómo usar Redis para sesiones en Django (django session redis)?
    • Instala django-redis y configura SESSION_ENGINE = 'django.contrib.sessions.backends.cache' junto con tu configuración de Redis en CACHES. Es la opción más recomendada para producción con alto tráfico.
  • ¿Qué pasa con las sesiones de usuarios anónimos?
    • Django crea sesiones también para usuarios no autenticados. Esto permite, por ejemplo, mantener un carrito de compras activo antes de que el usuario se registre o inicie sesión.
  • ¿Para qué sirve SESSION_SAVE_EVERY_REQUEST?
    • Por defecto, Django solo guarda la sesión si ha sido modificada. Si activas SESSION_SAVE_EVERY_REQUEST = True, la sesión se guardará en cada petición, actualizando también la fecha de expiración. Útil para mantener sesiones activas en aplicaciones con mucho tiempo entre peticiones.

Conclusión

Las sesiones en Django son una herramienta potente para mantener el estado del usuario en aplicaciones web sin comprometer la seguridad.

Entender cómo se crean, almacenan y gestionan —desde que el SessionMiddleware lee el sessionid hasta que guarda los cambios en request.session— permite construir sistemas de autenticación robustos y personalizaciones duraderas.

Dominar request.session fue clave para entender el "cerebro" de Django y su manera de conectar datos entre solicitudes. La regla de oro: guarda datos simples y serializables, protege siempre la cookie con HTTPS, y mantén la tabla django_session limpia con el comando clearsessions.

Siguiente paso, uso básico de la Shell - Consola Interactiva de Python en Django

Aprende a manejar el estado del usuario en Django con request.session. Diferencia entre sesiones y cookies, motores de almacenamiento (SESSION_ENGINE, Redis), configuración de SessionMiddleware y buenas prácticas de seguridad. Con ejemplos de código.


Únete a la comunidad de desarrolladores que han decidido dejar de picar código y empezar a construir productos reales. Recibe mis mejores trucos de arquitectura cada semana:

Acepto recibir anuncios de interes sobre este Blog.