Marco de caché de Django

Un equilibrio fundamental en sitios web dinámicos es que, bueno, son dinámicos. Cada vez que un usuario solicita una página, el servidor web realiza todo tipo de cálculos – desde consultas a la base de datos hasta renderizado de plantillas hasta lógica empresarial – para crear la página que ve tu visitante del sitio. Esto es mucho más caro, desde un punto de vista de sobrecarga de procesamiento, que su arreglo estándar de servidor de lectura de archivo en el sistema de archivos.

Para la mayoría de las aplicaciones web, esta sobrecarga no es un gran problema. La mayoría de las aplicaciones web no son washingtonpost.com o slashdot.org; son sitios pequeños a medianos con tráfico moderado. Pero para sitios de tráfico medio a alto, es esencial reducir la sobrecarga lo más posible.

Eso es donde entra en juego el caché.

Guardar algo en caché significa guardar el resultado de un cálculo caro para que no tengas que realizar el cálculo la próxima vez. Aquí tienes algunos pseudocódigo explicando cómo funcionaría esto para una página web generada dinámicamente:

given a URL, try finding that page in the cache
if the page is in the cache:
    return the cached page
else:
    generate the page
    save the generated page in the cache (for next time)
    return the generated page

Django viene con un sistema de caché robusto que te permite guardar páginas dinámicas para que no tengan que calcularse para cada solicitud. Para conveniencia, Django ofrece diferentes niveles de granularidad del caché: puedes cachear el resultado de vistas específicas, puedes cachear solo las partes difíciles de producir o puedes cachear todo tu sitio.

Django también funciona bien con «cachés downstream», como Squid y caches basados en el navegador. Estos son los tipos de caché que no controlas directamente pero a los cuales puedes proporcionar pistas (a través de encabezados HTTP) sobre qué partes de tu sitio deben ser almacenadas en caché, y cómo.

Ver también

La filosofía de diseño del Framework de Caché explica algunos de los decisiones de diseño del framework.

Configuración del cache

El sistema de caché requiere una pequeña cantidad de configuración. En concreto, debes decirle dónde debe vivir tu datos almacenados en caché – ya sea en una base de datos, en el sistema de archivos o directamente en memoria. Esta es una decisión importante que afecta al rendimiento del cache; sí, algunos tipos de caché son más rápidos que otros.

Tu preferencia de cache va en la CACHES configuración en tu archivo de configuración. Aquí tienes una explicación de todos los valores disponibles para CACHES.

Memcached

Memcached es un servidor de caché basado en memoria, completamente, originalmente desarrollado para manejar cargas altas en LiveJournal.com y posteriormente liberado bajo licencia abierta por Danga Interactive. Se utiliza en sitios como Facebook y Wikipedia para reducir el acceso a la base de datos y aumentar dramáticamente el rendimiento del sitio.

Memcached se ejecuta como un demonio y le es asignada una cantidad especificada de RAM. Solo proporciona una interfaz rápida para agregar, recuperar y eliminar datos en el cache. Todos los datos se almacenan directamente en memoria, por lo que no hay overhead de uso de base de datos o sistema de archivos.

Después de instalar Memcached mismo, necesitarás instalar un bindeo de Memcached. Hay varios bindeos Python para Memcached disponibles; los dos soportados por Django son pylibmc y pymemcache.

Para utilizar Memcached con Django:

  • Set BACKEND a django.core.cache.backends.memcached.PyMemcacheCache o django.core.cache.backends.memcached.PyLibMCCache (dependiendo de la unión de Memcached que hayas elegido)

  • Establece LOCATION a valores ip:port, donde ip es la dirección IP del demonio Memcached y port es el puerto en el que está ejecutándose Memcached, o a un valor unix:path, donde path es el camino al archivo de socket Unix de Memcached.

En este ejemplo, Memcached se está ejecutando en localhost (127.0.0.1) puerto 11211, utilizando la unión pymemcache:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
        "LOCATION": "127.0.0.1:11211",
    }
}

En este ejemplo, Memcached está disponible a través de un archivo de socket Unix local /tmp/memcached.sock utilizando la unión pymemcache:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
        "LOCATION": "unix:/tmp/memcached.sock",
    }
}

Una excelente característica de Memcached es su capacidad para compartir una caché sobre múltiples servidores. Esto significa que puedes ejecutar demonios de Memcached en múltiples máquinas, y el programa tratará al grupo de máquinas como una caché única, sin la necesidad de duplicar valores de caché en cada máquina. Para aprovechar esta característica, incluye todas las direcciones del servidor en LOCATION, ya sea como una cadena delimitada por punto y coma o comas, o como una lista.

En este ejemplo, la caché se comparte sobre instancias de Memcached que se ejecutan en la dirección IP 172.19.26.240 y 172.19.26.242, ambas en puerto 11211:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
        "LOCATION": [
            "172.19.26.240:11211",
            "172.19.26.242:11211",
        ],
    }
}

En el siguiente ejemplo, la caché se comparte sobre instancias de Memcached que se ejecutan en las direcciones IP 172.19.26.240 (puerto 11211), 172.19.26.242 (puerto 11212) y 172.19.26.244 (puerto 11213):

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
        "LOCATION": [
            "172.19.26.240:11211",
            "172.19.26.242:11212",
            "172.19.26.244:11213",
        ],
    }
}

Por defecto, el backend PyMemcacheCache establece las siguientes opciones (puedes sobrescribirlas en tu OPTIONS ):

"OPTIONS": {
    "allow_unicode_keys": True,
    "default_noreply": False,
    "serde": pymemcache.serde.pickle_serde,
}

Un punto final sobre Memcached es que la caché basada en memoria tiene una desventaja: porque los datos almacenados en caché se almacenan en memoria, los datos se perderán si tu servidor se cae. Claramente, la memoria no está destinada a almacenar datos permanentes, así que no confíes en la caché basada en memoria como único almacenamiento de datos. Sin duda alguna, ninguno de los backends de caché de Django deberían usarse para almacenamiento permanente – están todos diseñados para ser soluciones de caché, no de almacenamiento – pero señalamos esto aquí porque la caché basada en memoria es particularmente temporal.

Redis

Redis es una base de datos en memoria que se puede utilizar para caché. Para comenzar, necesitarás un servidor Redis ejecutándose localmente o en una máquina remota.

Una vez configurado el servidor Redis, deberás instalar los módulos de Python para Redis. redis-py es el módulo soportado nativamente por Django. Se recomienda también instalar el paquete hiredis-py.

Para utilizar Redis como backend de caché con Django:

Por ejemplo, si Redis está ejecutándose en localhost (127.0.0.1) en el puerto 6379:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
    }
}

A menudo los servidores Redis están protegidos con autenticación. Para suministrar un nombre de usuario y contraseña, agrega ambos en la LOCATION junto con la URL:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://username:password@127.0.0.1:6379",
    }
}

Si tienes varios servidores Redis configurados en modo de replicación, puedes especificarlos como una cadena delimitada por punto y coma o comas, o como una lista. Al utilizar múltiples servidores, las operaciones escrituras se realizan en el primer servidor (líder). Las operaciones de lectura se realizan en los otros servidores (replicas) elegidos al azar:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": [
            "redis://127.0.0.1:6379",  # leader
            "redis://127.0.0.1:6378",  # read-replica 1
            "redis://127.0.0.1:6377",  # read-replica 2
        ],
    }
}

Caché de base de datos

Django puede almacenar sus datos caché en la base de datos. Esto funciona mejor si tienes una base de datos rápida y bien indexada.

Para usar una tabla de base de datos como backend de caché:

  • Establece BACKEND a django.core.cache.backends.db.DatabaseCache

  • Establece LOCATION a tablename, el nombre de la tabla de base de datos. Este nombre puede ser lo que desees, siempre y cuando sea un nombre válido de tabla que no esté siendo utilizado en tu base de datos.

En este ejemplo, el nombre de la tabla del caché es my_cache_table:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.db.DatabaseCache",
        "LOCATION": "my_cache_table",
    }
}

A diferencia de otros backends de caché, el caché de base de datos no admite la eliminación automática de entradas expiradas a nivel de base de datos. En su lugar, las entradas de caché expiradas se eliminan cada vez que se llama a add(), set() o touch().

Creando la tabla del caché

Antes de usar el caché de base de datos, debes crear la tabla del caché con este comando:

python manage.py createcachetable

Esto crea una tabla en tu base de datos que está en el formato adecuado que espera el sistema de caché de base de datos de Django. El nombre de la tabla se toma de LOCATION.

Si estás utilizando múltiples caches de base de datos, createcachetable crea una tabla para cada cache.

Si estás utilizando múltiples bases de datos, createcachetable observa el método allow_migrate() de tus routers de base de datos (consultar a continuación).

Like migrar, crearcachetable no tocará una tabla existente. Sólo creará tablas faltantes.

Para imprimir el SQL que se ejecutaría en lugar de ejecutarlo, utilice la opción crearcachetable --dry-run.

Bases de datos múltiples

Si utiliza caché de base de datos con varias bases de datos, también necesitará configurar instrucciones de enrutado para su tabla de caché. Para fines de enrutado, la tabla de caché aparece como un modelo llamado CacheEntry, en una aplicación llamada django_cache. Este modelo no aparecerá en el cache de modelos, pero los detalles del modelo se pueden utilizar con fines de enrutado.

Por ejemplo, el siguiente router dirigiría todas las operaciones de lectura de caché a cache_replica, y todas las operaciones de escritura a cache_primary. La tabla de caché solo se sincronizará con cache_primary:

class CacheRouter:
    """A router to control all database cache operations"""

    def db_for_read(self, model, **hints):
        "All cache read operations go to the replica"
        if model._meta.app_label == "django_cache":
            return "cache_replica"
        return None

    def db_for_write(self, model, **hints):
        "All cache write operations go to primary"
        if model._meta.app_label == "django_cache":
            return "cache_primary"
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        "Only install the cache model on primary"
        if app_label == "django_cache":
            return db == "cache_primary"
        return None

Si no especifica direcciones de enrutado para el modelo de caché de base de datos, la backend de caché utilizará la base de datos por defecto.

Y si no utiliza el backend de caché de base de datos, no necesita preocuparse por proporcionar instrucciones de enrutado para el modelo de caché de base de datos.

Caché en filesystem

El backend basado en archivos serializa y almacena cada valor de caché como un archivo separado. Para utilizar este backend, establezca BACKEND a "django.core.cache.backends.filebased.FileBasedCache" y LOCATION en una carpeta adecuada. Por ejemplo, para almacenar datos de caché en /var/tmp/django_cache, utilice la siguiente configuración:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
        "LOCATION": "/var/tmp/django_cache",
    }
}

Si está en Windows, coloque la letra del disco al principio del camino, como este:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
        "LOCATION": "c:/foo/bar",
    }
}

El directorio de ruta debe ser absoluto – es decir, debe comenzar en la raíz de tu sistema de archivos. No importa si pones una barra al final de la configuración.

Asegúrate de que el directorio apuntado por esta configuración exista y sea legible y editable, o que pueda ser creado por el usuario del sistema bajo el cual se ejecuta tu servidor web. Continuando con el ejemplo anterior, si tu servidor se ejecuta como el usuario apache, asegúrate de que el directorio /var/tmp/django_cache exista y sea legible y editable por el usuario apache, o que pueda ser creado por el usuario apache.

Advertencia

Cuando la caché ubicación está contenida en MEDIA_ROOT, STATIC_ROOT o STATICFILES_FINDERS, los datos sensibles pueden estar expuestos.

Un atacante que obtenga acceso al archivo de caché no solo puede falsificar el contenido HTML, que tu sitio confiará, sino también ejecutar código arbitrario a distancia, ya que los datos se serializan utilizando pickle.

Advertencia

La caché en el sistema de archivos puede volverse lenta cuando se almacenan un gran número de archivos. Si te encuentras con este problema, considera utilizar una diferente mecanismo de caché. También puedes sobrescribir FileBasedCache y mejorar la estrategia de eliminación.

Caché en memoria local

Esta es la caché predeterminada si no se especifica otra en tu archivo de configuración. Si quieres aprovechar las ventajas de velocidad de la caché en memoria pero no tienes la capacidad de ejecutar Memcached, considera el backend de caché en memoria local. Esta caché es por proceso (ver más abajo) y segura para hilos. Para utilizarla, establece BACKEND a "django.core.cache.backends.locmem.LocMemCache". Por ejemplo:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
        "LOCATION": "unique-snowflake",
    }
}

La caché ubicación se utiliza para identificar las memorias de almacenamiento individuales. Si solo tienes una caché locmem, puedes omitir la ubicación; sin embargo, si tienes más de una caché en memoria local, necesitarás asignar un nombre a al menos una de ellas para mantenerlas separadas.

La caché utiliza una estrategia de eliminación LRU (menos recientemente utilizado).

Ten en cuenta que cada proceso tendrá su propia instancia privada de la caché, lo que significa que no es posible la caché entre procesos. Esto también significa que la caché en memoria local no es particularmente eficiente en términos de memoria, por lo que probablemente no sea una buena elección para entornos de producción. Es útil para el desarrollo.

Dummy caching (para desarrollo)

Finalmente, Django viene con una «caché de prueba» que no almacena realmente nada – simplemente implementa la interfaz de caché sin hacer nada.

Esto es útil si tienes un sitio de producción que utiliza una caché pesada en varios lugares pero un entorno de desarrollo/pruebas donde no quieres cachear y no quieres tener que cambiar tu código para especializar el último. Para activar la caché de prueba, establece BACKEND como se muestra a continuación:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.dummy.DummyCache",
    }
}

Uso de una caché personalizada

Si bien Django incluye soporte para varios backends de caché por defecto, a veces podrías querer usar un backend de caché personalizado. Para utilizar un backend de caché externo con Django, utiliza la ruta de importación Python como el BACKEND del CACHES configuración, como se muestra a continuación:

CACHES = {
    "default": {
        "BACKEND": "path.to.backend",
    }
}

Si estás creando tu propio backend, puedes utilizar los backends de caché estándar como implementaciones de referencia. Encontrarás el código en el directorio django/core/cache/backends/ del código fuente de Django.

Nota: Sin una razón realmente convincente, como un host que no admita dichos backends, debes seguir utilizando los backends de caché incluidos con Django. Han sido bien probados y están bien documentados.

Argumentos de la caché

Cada backend de caché puede darse argumentos adicionales para controlar el comportamiento de la caché. Estos argumentos se proporcionan como claves adicionales en la CACHES configuración. Los argumentos válidos son los siguientes:

  • TIMEOUT: El tiempo de espera predeterminado, en segundos, para utilizar con la caché. Este argumento tiene un valor por defecto de 300 segundos (5 minutos). Puedes establecer TIMEOUT en None para que, por defecto, las claves de caché nunca caducen. Un valor de 0 hace que las claves caducen inmediatamente (efectivamente «no cachear»).

  • :configuración:`OPCIÓNES <CACHES-OPTIONS>`: Cualquier opción que deba pasar a la backend de caché. La lista de opciones válidas variará con cada backend, y los backends de caché respaldados por una biblioteca de terceros pasarán sus opciones directamente a la biblioteca de caché subyacente.

    Los backends de caché que implementan su propia estrategia de eliminación (es decir, los backends locmem, filesystem y database) honrarán las siguientes opciones:

    • MAX_ENTRIES: El número máximo de entradas permitidas en la caché antes de eliminar valores antiguos. Este argumento tiene un valor por defecto de 300.

    • CULL_FREQUENCY: La fracción de entradas que se eliminan cuando MAX_ENTRIES se alcanza. La proporción real es 1 / CULL_FREQUENCY, por lo que establecer CULL_FREQUENCY en 2 eliminará la mitad de las entradas cuando MAX_ENTRIES se alcance. Este argumento debe ser un entero y tiene un valor por defecto de 3.

      Un valor de 0 para CULL_FREQUENCY significa que toda la caché se descargará cuando MAX_ENTRIES se alcance. En algunos backends (el database en particular) esto hace que la eliminación sea mucho más rápida a expensas de más accesos a la caché.

    Los backends Memcached y Redis pasan el contenido de :configuración:`OPCIÓNES <CACHES-OPTIONS>` como argumentos clave a los constructores del cliente, lo que permite un control avanzado del comportamiento del cliente. Para ver un ejemplo de uso, consulte la sección siguiente.

  • :configuración:`PREFIX DE CLAVE <CACHES-KEY_PREFIX>`: Una cadena que se incluirá automáticamente (se prependerá por defecto) a todas las claves de caché utilizadas por el servidor Django.

    Consulte la documentación sobre caché para obtener más información.

  • :configuración:`VERSION <CACHES-VERSION>`: El número de versión predeterminado para las claves de caché generadas por el servidor Django.

    Consulte la documentación sobre caché para obtener más información.

  • FUNCIÓN DE CLAVE Una cadena que contiene un camino de puntos a una función que define cómo componer una clave de prefijo, versión y clave en una clave de caché final.

    Consulte la documentación de cache para obtener más información.

En este ejemplo, se está configurando un backend de sistema de archivos con un tiempo de espera de 60 segundos y una capacidad máxima de 1000 elementos:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
        "LOCATION": "/var/tmp/django_cache",
        "TIMEOUT": 60,
        "OPTIONS": {"MAX_ENTRIES": 1000},
    }
}

Aquí tienes un ejemplo de configuración para un backend basado en pylibmc que habilita el protocolo binario, la autenticación SASL y el modo de comportamiento ketama

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.memcached.PyLibMCCache",
        "LOCATION": "127.0.0.1:11211",
        "OPTIONS": {
            "binary": True,
            "username": "user",
            "password": "pass",
            "behaviors": {
                "ketama": True,
            },
        },
    }
}

Aquí tienes un ejemplo de configuración para un backend basado en pymemcache que habilita la piscina de clientes (que puede mejorar el rendimiento manteniendo conectados a los clientes), trata errores de memcache/red como misses de caché y establece la bandera TCP_NODELAY en el socket de conexión

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
        "LOCATION": "127.0.0.1:11211",
        "OPTIONS": {
            "no_delay": True,
            "ignore_exc": True,
            "max_pool_size": 4,
            "use_pooling": True,
        },
    }
}

Aquí tienes un ejemplo de configuración para un backend basado en redis que selecciona la base de datos 10 (por defecto Redis viene con 16 bases lógicas de datos) y establece una clase personalizada de pool de conexiones (se utiliza por defecto redis.ConnectionPool )

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
        "OPTIONS": {
            "db": "10",
            "pool_class": "redis.BlockingConnectionPool",
        },
    }
}

La caché por sitio

Una vez configurada la caché, la forma más sencilla de utilizarla es cacheando toda tu aplicación. Debes agregar 'django.middleware.cache.UpdateCacheMiddleware' y 'django.middleware.cache.FetchFromCacheMiddleware' a tu MIDDLEWARE configuración, tal como se muestra en este ejemplo

MIDDLEWARE = [
    "django.middleware.cache.UpdateCacheMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.cache.FetchFromCacheMiddleware",
]

Nota

No, no es un error de ortografía: el middleware «update» debe estar primero en la lista y el middleware «fetch» debe estar último. Los detalles son un poco oscuros, pero si deseas leer la historia completa, consulta Orden de MIDDLEWARE a continuación.

Luego, agrega las siguientes configuraciones requeridas a tu archivo de configuración Django:

  • ALIAS_DE_CACHÉ – El alias de caché para usar para el almacenamiento.

  • SECONDS_DE_CACHÉ – El número entero de segundos cada página debe ser cacheado.

  • PREFIX_CLAVE_DE_CACHÉ – Si la caché se comparte entre múltiples sitios utilizando la misma instalación de Django, establezca esto en el nombre del sitio o alguna otra cadena que sea única para esta instancia de Django, para evitar colisiones de claves. Utilice una cadena vacía si no le importa.

FetchFromCacheMiddleware cachea respuestas GET y HEAD con estado 200, donde los encabezados de solicitud y respuesta lo permiten. Las respuestas a solicitudes para la misma URL con diferentes parámetros de consulta se consideran páginas únicas y se cachean por separado. Este middleware espera que una solicitud HEAD se responda con los mismos encabezados de respuesta que la solicitud GET correspondiente; en cuyo caso puede devolver una respuesta GET cacheada para solicitudes HEAD.

Además, UpdateCacheMiddleware establece automáticamente unos pocos encabezados en cada HttpResponse que afectan a las cachés downstream.

  • Establece el encabezado Expires en la fecha/hora actual más el tiempo definido por SECONDS_DE_CACHÉ.

  • Establece el encabezado Cache-Control para dar una edad máxima para la página – nuevamente, desde la configuración SECONDS_DE_CACHÉ.

Consulte Middleware para obtener más información sobre middleware.

Si una vista establece su propio tiempo de caducidad de caché (es decir, tiene una sección max-age en su encabezado Cache-Control) entonces la página se cacheará hasta el tiempo de caducidad, en lugar de SECONDS_DE_CACHÉ. Utilizando los decoradores en django.views.decorators.cache puedes establecer fácilmente el tiempo de caducidad de una vista (utilizando el decorador cache_control()) o deshabilitar la caché para una vista (utilizando el decorador never_cache()). Consulte la sección usando otros encabezados para obtener más información sobre estos decoradores.

Si USAR_I18N está establecido en True entonces la clave de caché generada incluirá el nombre del idioma activo – consulte también cómo Django descubre la preferencia de idioma). Esto te permite cachear fácilmente sitios multilingües sin tener que crear la clave de caché tú mismo.

Los textos traducidos manteniendo todas sus etiquetas intactas son:

La caché por vista

django.views.decorators.cache.cache_page(timeout, *, cache=None, key_prefix=None)

Una forma más detallada de utilizar el framework de caché es mediante la caché de los resultados individuales de las vistas. django.views.decorators.cache define un decorador cache_page que cacheará automáticamente la respuesta de la vista para ti:

from django.views.decorators.cache import cache_page


@cache_page(60 * 15)
def my_view(request): ...

cache_page toma un solo argumento: el tiempo de espera de la caché, en segundos. En el ejemplo anterior, el resultado de la vista my_view() se cacheará durante 15 minutos. (Nota que lo hemos escrito como 60 * 15 con fines de legibilidad. 60 * 15 se evaluará a 900 – es decir, 15 minutos multiplicado por 60 segundos por minuto.)

El tiempo de espera de la caché establecido por cache_page tiene prioridad sobre el directorio max-age del encabezado Cache-Control.

La caché por vista, como la caché por sitio, está claveada a partir de la URL. Si varias URLs apuntan a la misma vista, cada URL se cacheará por separado. Continuando con el ejemplo de la vista my_view, si tu configuración de URL se ve así:

urlpatterns = [
    path("foo/<int:code>/", my_view),
]

entonces las solicitudes a /foo/1/ y /foo/23/ se cachearán por separado, como podrías esperar. Pero una vez que se haya solicitado una URL en particular (por ejemplo, /foo/23/), las solicitudes posteriores a esa URL utilizarán la caché.

cache_page también puede tomar un argumento de palabra clave opcional, cache, que dirige al decorador a utilizar una caché específica (de tu configuración CACHES) cuando se cachean los resultados de las vistas. Por defecto, se utilizará la caché por defecto, pero puedes especificar cualquier caché que desees:

@cache_page(60 * 15, cache="special_cache")
def my_view(request): ...

También puedes sobrescribir el prefijo de la caché en una base por vista. cache_page toma un argumento de palabra clave opcional, key_prefix, que funciona de la misma manera que el CACHE_MIDDLEWARE_KEY_PREFIX configuración para el middleware. Puedes utilizarlo así:

@cache_page(60 * 15, key_prefix="site1")
def my_view(request): ...

Los argumentos key_prefix y cache pueden especificarse juntos. El argumento key_prefix y la KEY_PREFIX especificada bajo CACHES se concatenarán.

Además, cache_page establece automáticamente los encabezados Cache-Control y Expires en la respuesta que afectan a las cachés de downstream: downstream-caches.

Especificar caché por vista en la configuración de URLs

Las ejemplos de la sección anterior han codificado en duro el hecho de que la vista está caché, porque cache_page altera la función my_view en su lugar. Este enfoque acopla tu vista al sistema de caché, lo cual no es ideal por varias razones. Por ejemplo, podrías querer reutilizar las funciones de vistas en otro sitio sin caché o distribuir las vistas a personas que quieran utilizarlas sin ser cachéadas. La solución a estos problemas es especificar el caché por vista en la URLconf en lugar de junto a las funciones de vistas mismas.

Puedes hacerlo así envolviendo la función de vista con cache_page cuando se refiera a ella en el archivo URLconf. Aquí tienes el antiguo archivo URLconf desde antes:

urlpatterns = [
    path("foo/<int:code>/", my_view),
]

Aquí tienes lo mismo, con my_view envuelto en cache_page:

from django.views.decorators.cache import cache_page

urlpatterns = [
    path("foo/<int:code>/", cache_page(60 * 15)(my_view)),
]

Caché de fragmentos de plantilla

Si buscas aún más control, también puedes cachear fragmentos de plantilla utilizando la etiqueta de plantilla cache. Para dar a tu plantilla acceso a esta etiqueta, coloca {% load cache %} cerca de la parte superior de tu plantilla.

La etiqueta de plantilla {% cache %} almacena el contenido del bloque durante un período determinado de tiempo. Requiere como mínimo dos argumentos: el tiempo de espera de la caché, en segundos, y el nombre que se le dará a la fragmento de caché. El fragmento se almacena para siempre si timeout es None. El nombre se tomará tal cual, no utilices una variable. Por ejemplo:

{% load cache %}
{% cache 500 sidebar %}
    .. sidebar ..
{% endcache %}

A veces podrías querer cachear varias copias de un fragmento dependiendo de algunos datos dinámicos que aparecen dentro del fragmento. Por ejemplo, podrías querer una copia caché separada del menú lateral utilizado en el ejemplo anterior para cada usuario de tu sitio. Haz esto pasando uno o más argumentos adicionales, que pueden ser variables con o sin filtros, al tag de plantilla {% cache %} para identificar únicamente el fragmento de caché:

{% load cache %}
{% cache 500 sidebar request.user.username %}
    .. sidebar for logged-in user ..
{% endcache %}

Si se establece la configuración USE_I18N en True, el caché de middleware por sitio web respetará el idioma activo <i18n-cache-key>. Para el etiqueta de plantilla cache podrías utilizar una de las variables específicas de traducción <template-translation-vars> disponibles en las plantillas para lograr el mismo resultado:

{% load i18n %}
{% load cache %}

{% get_current_language as LANGUAGE_CODE %}

{% cache 600 welcome LANGUAGE_CODE %}
    {% translate "Welcome to example.com" %}
{% endcache %}

El timeout de la caché puede ser una variable de plantilla, siempre y cuando la variable de plantilla se resuelva a un valor entero. Por ejemplo, si la variable de plantilla my_timeout está configurada con el valor 600, entonces los siguientes dos ejemplos son equivalentes:

{% cache 600 sidebar %} ... {% endcache %}
{% cache my_timeout sidebar %} ... {% endcache %}

Esta característica es útil para evitar la repetición en las plantillas. Puedes establecer el tiempo de espera en una variable, en un lugar, y reutilizar ese valor.

Por defecto, la etiqueta de caché intentará utilizar la caché llamada «template_fragments». Si no existe tal caché, caerá en el uso de la caché predeterminada. Puedes seleccionar una caché de backend alternativo para usar con la palabra clave using, que debe ser el último argumento a la etiqueta.

{% cache 300 local-thing ...  using="localcache" %}

Se considera un error especificar un nombre de caché no configurado.

django.core.cache.utils.make_template_fragment_key(fragment_name, vary_on=None)

Si deseas obtener la llave de caché utilizada para una fragmento de plantilla, puedes utilizar make_template_fragment_key. fragment_name es el mismo que el segundo argumento a la etiqueta de caché; vary_on es una lista de todos los argumentos adicionales pasados a la etiqueta. Esta función puede ser útil para invalidar o sobrescribir un elemento de caché, por ejemplo:

>>> from django.core.cache import cache
>>> from django.core.cache.utils import make_template_fragment_key
# cache key for {% cache 500 sidebar username %}
>>> key = make_template_fragment_key("sidebar", [username])
>>> cache.delete(key)  # invalidates cached template fragment
True

La API de caché de bajo nivel

A veces, cacheando toda la página renderizada no te da mucha ventaja y es, en realidad, una excesiva sobreabundancia.

Quizás, por ejemplo, tu sitio incluye una vista cuyos resultados dependen de varias consultas costosas, los resultados de las cuales cambian a intervalos diferentes. En este caso, no sería ideal utilizar la cacheo de página completa que ofrecen las estrategias de caché por sitio o por vista, porque no querrías cachear el resultado completo (ya que algunos de los datos cambian con frecuencia), pero aún así querrías cachear los resultados que rara vez cambian.

Para casos como este, Django expone una API de caché de bajo nivel. Puedes utilizar esta API para almacenar objetos en la caché con cualquier nivel de grano que desees. Puedes cachear cualquier objeto Python que pueda ser picklado de manera segura: cadenas, diccionarios, listas de objetos de modelo y así sucesivamente. (La mayoría de los objetos comunes de Python pueden ser picklados; consulta la documentación de Python para obtener más información sobre el pickling.)

Accediendo a la caché

django.core.cache.caches

Puedes acceder a las cachés configuradas en la CACHES mediante un objeto similar a un diccionario: django.core.cache.caches. Las solicitudes repetidas para el mismo alias en el mismo hilo devolverán el mismo objeto.

>>> from django.core.cache import caches
>>> cache1 = caches["myalias"]
>>> cache2 = caches["myalias"]
>>> cache1 is cache2
True

Si la clave nombrada no existe, se levantará una InvalidCacheBackendError.

Para proporcionar seguridad de hilos, se devolverá una instancia diferente del backend de caché para cada hilo.

django.core.cache.cache

Como atajo, el caché por defecto está disponible como django.core.cache.cache:

>>> from django.core.cache import cache

Este objeto es equivalente a caches['default'].

Uso básico

La interfaz básica es:

cache.set(key, value, timeout=DEFAULT_TIMEOUT, version=None)
>>> cache.set("my_key", "hello, world!", 30)
cache.get(key, default=None, version=None)
>>> cache.get("my_key")
'hello, world!'

key debe ser una str, y value puede ser cualquier objeto Python picklable.

El argumento timeout es opcional y tiene como valor por defecto el argumento timeout del backend apropiado en la CACHES configuración (explicada anteriormente). Es el número de segundos que el valor debe estar almacenado en la caché. Pasar None para timeout cacheará el valor para siempre. Un timeout de 0 no cacheará el valor.

Si el objeto no existe en la caché, cache.get() devuelve None:

>>> # Wait 30 seconds for 'my_key' to expire...
>>> cache.get("my_key")
None

Si necesitas determinar si el objeto existe en la caché y has almacenado un valor literal None, utiliza un objeto sentinela como valor por defecto:

>>> sentinel = object()
>>> cache.get("my_key", sentinel) is sentinel
False
>>> # Wait 30 seconds for 'my_key' to expire...
>>> cache.get("my_key", sentinel) is sentinel
True

cache.get() puede tomar un argumento default. Este especifica qué valor devolver si el objeto no existe en la caché:

>>> cache.get("my_key", "has expired")
'has expired'
cache.add(key, value, timeout=DEFAULT_TIMEOUT, version=None)

Para agregar una clave solo si ya no existe, utiliza el método add(). Recibe los mismos parámetros que set(), pero no intentará actualizar la caché si la clave especificada ya está presente:

>>> cache.set("add_key", "Initial value")
>>> cache.add("add_key", "New value")
>>> cache.get("add_key")
'Initial value'

Si necesitas saber si add() almacenó un valor en la caché, puedes comprobar el valor de retorno. Devolverá True si se almacenó el valor, False en caso contrario:

cache.get_or_set(key, default, timeout=DEFAULT_TIMEOUT, version=None)

Si deseas obtener el valor de una clave o establecer un valor si la clave no está en la caché, existe el método get_or_set(). Recibe los mismos parámetros que get(), pero el valor por defecto se establece como el nuevo valor de la caché para esa clave, en lugar de devolverlo:

>>> cache.get("my_new_key")  # returns None
>>> cache.get_or_set("my_new_key", "my new value", 100)
'my new value'

También puedes pasar cualquier función callable como un valor default:

>>> import datetime
>>> cache.get_or_set("some-timestamp-key", datetime.datetime.now)
datetime.datetime(2014, 12, 11, 0, 15, 49, 457920)
cache.get_many(keys, version=None)

También existe una interfaz get_many() que solo consulta la caché una vez. get_many() devuelve un diccionario con todas las claves que solicitaste y que realmente existen en la caché (y no han expirado):

>>> cache.set("a", 1)
>>> cache.set("b", 2)
>>> cache.set("c", 3)
>>> cache.get_many(["a", "b", "c"])
{'a': 1, 'b': 2, 'c': 3}
cache.set_many(dict, timeout)

Para establecer múltiples valores de manera más eficiente, utiliza set_many() para pasar un diccionario de pares clave-valor:

>>> cache.set_many({"a": 1, "b": 2, "c": 3})
>>> cache.get_many(["a", "b", "c"])
{'a': 1, 'b': 2, 'c': 3}

Al igual que cache.set(), set_many() recibe un parámetro timeout opcional:

En backends compatibles (memcached), set_many() devuelve una lista de claves que fallaron al ser insertadas.

cache.delete(key, version=None)

Puedes eliminar claves explícitamente con delete() para borrar el caché de un objeto en particular:

>>> cache.delete("a")
True

delete() devuelve True si la clave se borró correctamente, False en caso contrario.

cache.delete_many(keys, version=None)

Si deseas borrar varias claves a la vez, delete_many() puede recibir una lista de claves para ser borradas:

>>> cache.delete_many(["a", "b", "c"])
cache.clear()

Finalmente, si deseas borrar todas las claves del caché, utiliza cache.clear(). Ten cuidado con esto; clear() eliminará todo del caché, no solo las claves establecidas por tu aplicación:

>>> cache.clear()
cache.touch(key, timeout=DEFAULT_TIMEOUT, version=None)

cache.touch() establece una nueva expiración para una clave. Por ejemplo, para actualizar una clave para que expire 10 segundos desde ahora:

>>> cache.touch("a", 10)
True

Como otros métodos, el argumento timeout es opcional y tiene como valor predeterminado la opción TIMEOUT del backend apropiado en la configuración CACHES de tu proyecto.

touch() devuelve True si la clave se tocó correctamente, False en caso contrario.

cache.incr(key, delta=1, version=None)
cache.decr(key, delta=1, version=None)

También puedes incrementar o decrementar una clave que ya existe utilizando los métodos incr() o decr(), respectivamente. Por defecto, el valor de caché existente se incrementará o decrementará en 1. Otros valores de incremento/decremento pueden especificarse proporcionando un argumento a la llamada de incremento/decremento. Se levantará una ValueError si intentas incrementar o decrementar una clave del caché inexistente:

>>> cache.set("num", 1)
>>> cache.incr("num")
2
>>> cache.incr("num", 10)
12
>>> cache.decr("num")
11
>>> cache.decr("num", 5)
6

Nota

Los métodos incr()/decr() no están garantizados como átomos. En aquellos backends que admiten incrementos y decrementos átomos (principalmente, el backend de memcached), las operaciones de incremento y decremento serán átomas. Sin embargo, si el backend no proporciona nativamente una operación de incremento/decremento, se implementará utilizando un paso de recuperar/actualizar.

cache.close()

Puedes cerrar la conexión con tu caché con close() si está implementado por el backend del caché.

>>> cache.close()

Nota

Los textos traducidos son:

Nota

Las variantes asíncronas de los métodos base se prefijan con a, por ejemplo cache.aadd() o cache.adelete_many(). Consulte Soporte asíncrono para obtener más detalles.

Prefijo de claves de caché

Si estás compartiendo una instancia de caché entre servidores, o entre tus entornos de producción y desarrollo, es posible que los datos almacenados por un servidor sean utilizados por otro. Si el formato de los datos almacenados en caché es diferente entre servidores, esto puede dar lugar a algunos problemas muy difíciles de diagnosticar.

Para evitar esto, Django proporciona la capacidad de prefijar todas las claves de caché utilizadas por un servidor. Cuando una clave de caché particular se almacena o se recupera, Django prefixará automáticamente la clave de caché con el valor del ajuste de caché KEY_PREFIX.

Al asegurarte de que cada instancia de Django tenga un diferente KEY_PREFIX, puedes asegurar que no habrá colisiones en los valores de caché.

Versión del caché

Cuando cambies el código ejecutado que utiliza valores de caché, es posible que necesites eliminar cualquier valor de caché existente. La forma más fácil de hacer esto es vaciar el caché completo, pero esto puede dar lugar a la pérdida de valores de caché que todavía son válidos y útiles.

Django proporciona una mejor manera de dirigir valores de caché individuales. El marco de trabajo de caché de Django tiene un identificador de versión global, especificado utilizando el ajuste de caché VERSION. El valor de este ajuste se combina automáticamente con el prefijo del caché y la clave de caché proporcionada por el usuario para obtener la clave de caché final.

Por defecto, cualquier solicitud de clave incluirá automáticamente la versión predeterminada de la clave de caché del sitio. Sin embargo, las funciones de caché primitivas incluyen un argumento version, por lo que puedes especificar una versión particular de la clave de caché para establecer o recuperar. Por ejemplo:

>>> # Set version 2 of a cache key
>>> cache.set("my_key", "hello world!", version=2)
>>> # Get the default version (assuming version=1)
>>> cache.get("my_key")
None
>>> # Get version 2 of the same key
>>> cache.get("my_key", version=2)
'hello world!'

La traducción de los textos es la siguiente:

>>> # Increment the version of 'my_key'
>>> cache.incr_version("my_key")
>>> # The default version still isn't available
>>> cache.get("my_key")
None
# Version 2 isn't available, either
>>> cache.get("my_key", version=2)
None
>>> # But version 3 *is* available
>>> cache.get("my_key", version=3)
'hello world!'

Transformación de la clave de caché

Como se describe en las dos secciones anteriores, la clave de caché proporcionada por el usuario no se utiliza tal cual – se combina con la prefijo de caché y la versión de la clave para proporcionar una clave de caché final. Por defecto, los tres partes se unen utilizando colones para producir una cadena final:

def make_key(key, key_prefix, version):
    return "%s:%s:%s" % (key_prefix, version, key)

Si deseas combinar las partes de manera diferente o aplicar otra procesamiento a la clave final (por ejemplo, tomar el digest de hash de las partes de la clave), puedes proporcionar una función de clave personalizada.

La configuración de caché KEY_FUNCTION especifica un camino puntoado a una función que coincide con el prototipo de make_key() arriba. Si se proporciona, esta función de clave personalizada se utilizará en lugar de la función por defecto para combinar claves.

Advertencias sobre claves de caché

Memcached, el backend de caché de producción más comúnmente utilizado, no permite claves de caché más largas que 250 caracteres o conteniendo espacios en blanco o caracteres de control, y utilizar tales claves causará una excepción. Para fomentar código portátil para cachés y minimizar sorpresas desagradables, los otros backends de caché integrados emiten una advertencia (django.core.cache.backends.base.CacheKeyWarning) si se utiliza una clave que causaría un error en memcached.

Si estás utilizando un backend de producción que puede aceptar una gama más amplia de claves (un backend personalizado o uno de los backends integrados no basado en Memcached), y deseas utilizar esta gama más amplia sin advertencias, puedes silenciar CacheKeyWarning con este código en el módulo management de uno de tus INSTALLED_APPS:

import warnings

from django.core.cache import CacheKeyWarning

warnings.simplefilter("ignore", CacheKeyWarning)

Si deseas proporcionar lógica de validación de claves personalizada para uno de los backends integrados en lugar de silenciar las advertencias, puedes sobrescribirlo, sobreescribiendo solo el método validate_key, y seguir las instrucciones para usar un backend de caché personalizado. Por ejemplo, para hacer esto con el backend locmem, coloca este código en un módulo:

from django.core.cache.backends.locmem import LocMemCache


class CustomLocMemCache(LocMemCache):
    def validate_key(self, key):
        """Custom validation, raising exceptions or warnings as needed."""
        ...

…y utiliza la ruta puntoada a Python para esta clase en la parte BACKEND de tu configuración CACHES.

Soporte asíncrono

Django tiene soporte de desarrollo para backends de caché asíncronos, pero no soporta todavía la caché asíncrona. Vendrá en una futura versión.

django.core.cache.backends.base.BaseCache tiene variantes asíncronas de todos los métodos base. Por convención, las versiones asíncronas de todos los métodos están prefijadas con a. Los argumentos por defecto para ambas variantes son los mismos:

>>> await cache.aset("num", 1)
>>> await cache.ahas_key("num")
True

Cachés downstream

Hasta ahora, este documento se ha centrado en la caché de datos propios. Pero otro tipo de caché es relevante para el desarrollo web: la caché realizada por «cachés downstream». Estos son sistemas que cachean páginas para los usuarios incluso antes de que llegue la solicitud a su sitio web.

Aquí hay unos pocos ejemplos de cachés downstream:

  • Al utilizar HTTP, tu proveedor de servicios de Internet (ISP) puede cachear ciertas páginas, por lo que si solicitaste una página desde http://example.com/, tu ISP te enviaría la página sin tener que acceder a ejemplo.com directamente. Los mantenedores de ejemplo.com no tienen conocimiento de esta caché; el ISP se sienta entre ejemplo.com y tu navegador web, gestionando toda la caché de manera transparente. Tal caché no es posible bajo HTTPS ya que constituiría un ataque en el medio.

  • Tu sitio web Django puede estar detrás de un caché proxy, como Squid Web Proxy Cache (http://www.squid-cache.org/), que cachea páginas para mejorar el rendimiento. En este caso, cada solicitud primero sería gestionada por el proxy y luego se pasaría a tu aplicación solo si era necesario.

  • También tu navegador web cachea páginas. Si una página web envía los encabezados adecuados, tu navegador utilizará la copia local cacheada para solicitudes posteriores a esa página, sin ni siquiera contactar con la página web de nuevo para ver si ha cambiado.

La caché downstream es un buen aumento de eficiencia, pero hay un peligro: muchas páginas web tienen contenido que difiere en función de la autenticación y una variedad de otros factores, y los sistemas de caché que guardan páginas basándose únicamente en URLs podrían exponer datos incorrectos o sensibles a visitantes posteriores a esas páginas.

Por ejemplo, si operas un sistema web de correo electrónico, entonces el contenido de la página «inbox» depende del usuario que está conectado. Si un ISP cacheaba tu sitio de manera ciega, entonces el primer usuario que se conectó a través de ese ISP tendría su página de buzón personal cacheada para visitantes posteriores al sitio. Eso no es cool.

La solución a este problema la proporciona HTTP. Un número de encabezados HTTP existen para instruir a los caches descendentes que diferencien sus contenidos de cache dependiendo de variables designadas, y para decir a los mecanismos de caching que no cachearán páginas particulares. Vamos a ver algunos de estos encabezados en las secciones siguientes.

Utilizando encabezados Vary

El encabezado Vary define qué encabezados de solicitud un mecanismo de cache debe tener en cuenta al construir su clave de cache. Por ejemplo, si el contenido de una página web depende de la preferencia lingüística de un usuario, se dice que la página «varía por idioma».

Por defecto, el sistema de cache de Django crea sus claves de cache utilizando la URL solicitada completamente cualificada – e.g., "https://www.example.com/stories/2005/?order_by=author". Esto significa que cada solicitud a esa URL utilizará la misma versión caché, independientemente de las diferencias en los agentes de usuario como cookies o preferencias lingüísticas. Sin embargo, si esta página produce contenido diferente basado en alguna diferencia en encabezados de solicitud – como una cookie, un idioma o un agente de usuario – necesitarás utilizar el encabezado Vary para decir a los mecanismos de caching que el contenido de la página depende de esas cosas.

Para hacer esto en Django, utiliza el decorador de vista conveniente django.views.decorators.vary.vary_on_headers() como se muestra a continuación:

from django.views.decorators.vary import vary_on_headers


@vary_on_headers("User-Agent")
def my_view(request): ...

En este caso, un mecanismo de caching (como el middleware de cache propio de Django) cacheará una versión separada de la página para cada agente de usuario único.

La ventaja de utilizar el decorador vary_on_headers en lugar de establecer manualmente el encabezado Vary (utilizando algo como response.headers['Vary'] = 'user-agent') es que el decorador añade al encabezado Vary (que puede ya existir), en lugar de establecerlo desde cero y potencialmente sobreescribir cualquier cosa que estuviera allí.

Puedes pasar múltiples encabezados a vary_on_headers():

@vary_on_headers("User-Agent", "Cookie")
def my_view(request): ...

Esto dice a los caches descendentes que varíen por ambos, lo que significa que cada combinación de agente de usuario y cookie tendrá su propia caché. Por ejemplo, una solicitud con el agente de usuario Mozilla y el valor de la cookie foo=bar se considerará diferente de una solicitud con el agente de usuario Mozilla y el valor de la cookie foo=ham.

Porque variar por cookie es tan común, hay un decorador django.views.decorators.vary.vary_on_cookie(). Estos dos vistas son equivalentes:

@vary_on_cookie
def my_view(request): ...


@vary_on_headers("Cookie")
def my_view(request): ...

Los textos traducidos son:

También puedes utilizar una función auxiliar, django.utils.cache.patch_vary_headers(), directamente. Esta función establece o agrega el encabezado Vary. Por ejemplo:

from django.shortcuts import render
from django.utils.cache import patch_vary_headers


def my_view(request):
    ...
    response = render(request, "template_name", context)
    patch_vary_headers(response, ["Cookie"])
    return response

patch_vary_headers toma como primer argumento una instancia de HttpResponse y como segundo argumento una lista/tupla de nombres de encabezados no sensibles al caso.

Para más información sobre los encabezados Vary, consulta la especificación oficial de Vary.

Controlando la caché: Utilizando otros encabezados

Otros problemas con la caché son la privacidad de los datos y la pregunta de dónde deben almacenarse los datos en una cascada de caches.

Un usuario suele enfrentar dos tipos de caches: su propia caché del navegador (una caché privada) y la caché de su proveedor (una caché pública). Una caché pública se utiliza por múltiples usuarios y está controlada por alguien más. Esto plantea problemas con los datos sensibles–no quieres, por ejemplo, que tu número de cuenta bancaria esté almacenado en una caché pública. Por lo tanto, las aplicaciones web necesitan una forma de indicar a las caches qué datos son privados y cuáles son públicos.

La solución es indicar que la caché de una página debe ser «privada». Para hacer esto en Django, utiliza el decorador de vista cache_control(). Ejemplo:

from django.views.decorators.cache import cache_control


@cache_control(private=True)
def my_view(request): ...

Este decorador se encarga de enviar el encabezado HTTP apropiado detrás de escena.

Ten en cuenta que las configuraciones de control de caché «privada» y «pública» son mutuamente excluyentes. El decorador asegura que la directiva «pública» se elimine si debe establecerse «privada» (y viceversa). Un ejemplo de uso de las dos directivas sería un sitio web de blog que ofrece tanto entradas privadas como públicas. Las entradas públicas pueden almacenarse en cualquier caché compartida. El siguiente código utiliza patch_cache_control(), la forma manual de modificar el encabezado de control de caché (es internamente llamada por el decorador cache_control()):

from django.views.decorators.cache import patch_cache_control
from django.views.decorators.vary import vary_on_cookie


@vary_on_cookie
def list_blog_entries_view(request):
    if request.user.is_anonymous:
        response = render_only_public_entries()
        patch_cache_control(response, public=True)
    else:
        response = render_private_and_public_entries(request.user)
        patch_cache_control(response, private=True)

    return response

Puedes controlar las cachés de otros niveles en otras formas (consulte RFC 9111 para obtener detalles sobre la caché HTTP). Por ejemplo, incluso si no utilizas el marco de trabajo de caché del lado del servidor de Django, todavía puedes pedir a los clientes que cacheen una vista durante un cierto período de tiempo con la directiva max-age <9111#section-5.2.2.1>:

from django.views.decorators.cache import cache_control


@cache_control(max_age=3600)
def my_view(request): ...

( Si utilizas el middleware de caché, ya establece el max-age con el valor del parámetro de configuración CACHE_MIDDLEWARE_SECONDS. En ese caso, la directiva personalizada max_age del decorador cache_control <django.views.decorators.cache.cache_control>` tendrá prioridad y los valores de encabezados se combinarán correctamente.)

Cualquier directiva válida de respuesta Cache-Control es válida en cache_control(). Aquí hay algunos ejemplos adicionales:

  • no_transform=True

  • must_revalidate=True

  • stale_while_revalidate=num_seconds

  • no_cache=True

La lista completa de directivas conocidas se puede encontrar en el registro IANA (tenga en cuenta que no todas ellas se aplican a las respuestas).

Si deseas utilizar encabezados para deshabilitar la caché por completo, never_cache() es un decorador de vistas que agrega encabezados para asegurarse de que la respuesta no será cacheada por los navegadores u otros caches. Ejemplo:

from django.views.decorators.cache import never_cache


@never_cache
def myview(request): ...

Orden de MIDDLEWARE

Si utilizas middleware de caché, es importante colocar cada mitad en el lugar correcto dentro del MIDDLEWARE configuración. Eso se debe a que el middleware de caché necesita saber qué encabezados por los cuales variar la almacenamiento de caché. El middleware siempre agrega algo al encabezado de respuesta Vary cuando puede.

UpdateCacheMiddleware se ejecuta durante la fase de respuesta, donde el middleware se ejecuta en orden inverso, así que un elemento en la parte superior de la lista corre último durante la fase de respuesta. Por lo tanto, debes asegurarte de que UpdateCacheMiddleware aparezca antes de cualquier otro middleware que pueda agregar algo al encabezado Vary. Los siguientes módulos de middleware hacen eso:

  • SessionMiddleware agrega Cookie

  • GZipMiddleware agrega Accept-Encoding

  • LocaleMiddleware agrega Accept-Language

Por otro lado, FetchFromCacheMiddleware, se ejecuta durante la fase de solicitud, donde el middleware se aplica primero-a-último, así que un elemento en la parte superior de la lista corre primero durante la fase de solicitud. El FetchFromCacheMiddleware también necesita correr después del otro middleware actualiza el encabezado Vary, por lo tanto FetchFromCacheMiddleware debe ser después de cualquier elemento que haga eso.