Configuración de caché
Django soporta múltiples backends de caché. Redis es la opción recomendada en producción:
pip install django-redis

# settings.py
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
'COMPRESSOR': 'django_redis.compressors.zlib.ZlibCompressor',
},
'KEY_PREFIX': 'miapp',
'TIMEOUT': 300, # 5 minutos por defecto
},
'sesiones': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/2',
'OPTIONS': {'CLIENT_CLASS': 'django_redis.client.DefaultClient'},
}
}
# Para desarrollo (caché en memoria, sin Redis)
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
}
}
cache_page: caché de vista completa
from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_cookie, vary_on_headers
@cache_page(60 * 15) # 15 minutos
def catalogo_publico(request):
productos = Producto.objects.filter(activo=True).select_related('categoria')
return render(request, 'catalogo.html', {'productos': productos})
# Con vary: diferente caché según el idioma
@cache_page(60 * 60)
@vary_on_headers('Accept-Language')
def pagina_inicio(request):
return render(request, 'inicio.html')
# En CBV
from django.utils.decorators import method_decorator
@method_decorator(cache_page(60 * 15), name='dispatch')
class CatalogoView(ListView):
model = Producto
template_name = 'catalogo.html'
API de bajo nivel
from django.core.cache import cache
# Almacenar en caché
cache.set('productos_destacados', lista_productos, timeout=60 * 30) # 30 min
cache.set('config_sitio', config_dict) # Usa el timeout por defecto
# Recuperar de caché
productos = cache.get('productos_destacados')
if productos is None:
# Cache miss: calcular y almacenar
productos = Producto.objects.filter(destacado=True).select_related('categoria')
cache.set('productos_destacados', list(productos), timeout=60 * 30)
# Patrón cache-or-set
from django.core.cache import cache
def obtener_estadisticas():
clave = 'estadisticas_globales'
stats = cache.get(clave)
if stats is None:
stats = {
'total_usuarios': User.objects.count(),
'total_productos': Producto.objects.filter(activo=True).count(),
'total_pedidos_hoy': Pedido.objects.filter(fecha__date=date.today()).count(),
}
cache.set(clave, stats, timeout=60 * 5) # 5 minutos
return stats
# Eliminar de caché (invalidación)
cache.delete('productos_destacados')
cache.delete_many(['clave1', 'clave2', 'clave3'])
# Incrementar contador atómicamente
cache.set('visitas_articulo_1', 0, timeout=None)
cache.incr('visitas_articulo_1')
# Comprobar si existe
if cache.has_key('productos_destacados'):
pass
Fragment caching en plantillas
{% load cache %}
<!-- Cachear un fragment durante 30 minutos -->
{% cache 1800 menu_navegacion %}
<nav>
{% for categoria in categorias %}
<a href="{{ categoria.get_absolute_url }}">{{ categoria.nombre }}</a>
{% endfor %}
</nav>
{% endcache %}
<!-- Caché diferente por usuario -->
{% cache 600 carrito_usuario request.user.pk %}
<div class="carrito">
{{ carrito|length }} producto(s) — {{ carrito.total }} €
</div>
{% endcache %}
<!-- Caché diferente por idioma -->
{% cache 3600 pagina_inicio LANGUAGE_CODE %}
<div class="bienvenida">
{% trans "Bienvenido a nuestra tienda" %}
</div>
{% endcache %}
Invalidación con señales
La invalidación de caché es el problema más difícil del caching. Usar señales para invalidar automáticamente:
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from django.core.cache import cache
@receiver([post_save, post_delete], sender=Producto)
def invalidar_cache_catalogo(sender, **kwargs):
"""Invalida la caché del catálogo cuando cambia algún producto."""
cache.delete_many([
'productos_destacados',
'catalogo_publico',
'estadisticas_globales',
])
# Invalidar por patrón con django-redis
cache.delete_pattern('catalogo:*')
@receiver(post_save, sender=Categoria)
def invalidar_cache_categorias(sender, **kwargs):
cache.delete('menu_navegacion')
cache.delete('categorias_activas')
Caché por versión
Django soporta versionado de caché para invalidar grupos de claves:
# settings.py
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
'VERSION': 1,
}
}
# Invalidar toda la versión incrementando el número
from django.core.cache import cache
cache.clear() # Elimina todo el caché de la versión actual
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, Django 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 Django
Explora más contenido relacionado con Django y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Configurar el backend de caché con Redis en settings.py. Aplicar caché a nivel de vista completa con @cache_page. Usar la API de bajo nivel cache.get(), cache.set() y cache.delete(). Implementar fragment caching en plantillas con {% cache %}. Gestionar la invalidación de caché cuando los datos cambian.