Система кэширования Django
Основной компромисс при создании динамических веб-сайтов заключается в том, что они динамичны. Каждый раз, когда пользователь запрашивает страницу, веб-сервер выполняет различные вычисления — от запросов к базе данных до рендеринга шаблонов и бизнес-логики — для создания страницы, которую видит посетитель сайта. Это значительно дороже с точки зрения накладных расходов на обработку, чем стандартная схема сервера, читающего файл из файловой системы.
Для большинства веб-приложений эти накладные расходы не являются проблемой. Большинство веб-приложений не являются washingtonpost.com или slashdot.org; это небольшие или средние сайты со средним трафиком. Но для сайтов со средним или высоким трафиком крайне важно свести к минимуму эти накладные расходы.
Вот где пригождается кэширование.
Кэширование — это сохранение результата дорогостоящих вычислений, чтобы не выполнять их повторно. Вот псевдокод, объясняющий, как это работает для динамически генерируемой веб-страницы:
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 поставляется с надежной системой кэширования, позволяющей сохранять динамические страницы, чтобы их не приходилось вычислять для каждого запроса. Для удобства Django предлагает различные уровни детализации кэширования: вы можете кэшировать результаты определенных представлений, кэшировать только сложные для вычисления части или кэшировать весь сайт.
Django также хорошо работает с «нисходящими» кэшами, такими как Squid и кэши браузера. Это типы кэшей, которыми вы не управляете напрямую, но можете предоставлять им подсказки (через HTTP-заголовки) о том, какие части вашего сайта следует кэшировать и как.
См. также
В Философии проектирования системы кэширования объясняются некоторые решения, принятые при разработке фреймворка.
Настройка кэша
Система кэширования требует небольшого объёма настроек. В частности, необходимо указать, где должны храниться кэшированные данные — в базе данных, на файловой системе или непосредственно в оперативной памяти. Это важное решение, влияющее на производительность кэша; некоторые типы кэшей быстрее других.
Ваши предпочтения относительно кэша указываются в настройке CACHES в файле настроек. Вот описание всех доступных значений для CACHES.
Memcached
Memcached — это кэширующий сервер, работающий исключительно в оперативной памяти, первоначально разработанный для обработки больших нагрузок на LiveJournal.com, а затем открытый для общего доступа компанией Danga Interactive. Он используется такими сайтами, как Facebook и Wikipedia, для уменьшения доступа к базе данных и значительного повышения производительности сайта.
Memcached работает в виде демона и выделяет указанный объём оперативной памяти. Всё, что он делает, это предоставляет быстрый интерфейс для добавления, извлечения и удаления данных из кэша. Все данные хранятся непосредственно в оперативной памяти, поэтому нет накладных расходов на использование базы данных или файловой системы.
После установки самого Memcached потребуется установить привязку Memcached. Существует несколько доступных Python-привязок к Memcached; две поддерживаемые Django — pylibmc и pymemcache.
Чтобы использовать Memcached с Django:
- Установите
BACKENDв значениеdjango.core.cache.backends.memcached.PyMemcacheCacheилиdjango.core.cache.backends.memcached.PyLibMCCache(в зависимости от выбранной привязки Memcached) - Установите
LOCATIONвip:portзначения, гдеip— IP-адрес демона Memcached, аport— порт, на котором работает Memcached, или вunix:pathзначение, гдеpath— путь к файлу сокета Memcached.
В этом примере Memcached работает на локальном хосте (127.0.0.1) на порту 11211, используя привязку pymemcache:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": "127.0.0.1:11211",
}
}
В этом примере Memcached доступен через локальный файл сокета /tmp/memcached.sock, используя привязку pymemcache:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": "unix:/tmp/memcached.sock",
}
}
Отличной особенностью Memcached является его способность к совместному использованию кэша на нескольких серверах. Это означает, что вы можете запускать демоны Memcached на нескольких машинах, и программа будет рассматривать группу машин как один кэш, без необходимости дублирования значений кэша на каждой машине. Чтобы воспользоваться этой функцией, включите все адреса серверов в LOCATION, либо как строку, разделенную точкой с запятой или запятыми, либо как список.
В этом примере кэш разделяется между экземплярами Memcached, работающими по IP-адресам 172.19.26.240 и 172.19.26.242, оба на порту 11211:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": [
"172.19.26.240:11211",
"172.19.26.242:11211",
],
}
}
В следующем примере кэш разделяется между экземплярами Memcached, работающими по IP-адресам 172.19.26.240 (порт 11211), 172.19.26.242 (порт 11212) и 172.19.26.244 (порт 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",
],
}
}
По умолчанию кэш PyMemcacheCache устанавливает следующие параметры (вы можете переопределить их в OPTIONS):
"OPTIONS": {
"allow_unicode_keys": True,
"default_noreply": False,
"serde": pymemcache.serde.pickle_serde,
}
Ещё один момент о Memcached: кэширование в памяти имеет недостаток: поскольку кэшированные данные хранятся в оперативной памяти, при сбое сервера данные будут потеряны. Очевидно, что оперативная память не предназначена для постоянного хранения данных, поэтому не полагайтесь на кэширование в памяти как на единственный способ хранения данных. Несомненно, ни один из кэшей Django не следует использовать для постоянного хранения — все они предназначены для кэширования, а не хранения — но мы указываем на это, потому что кэширование в памяти особенно временное.
Redis
Redis — это база данных в оперативной памяти, которая может использоваться для кэширования. Для начала потребуется запустить Redis-сервер локально или на удалённой машине.
После настройки Redis-сервера вам потребуется установить Python-привязку к Redis. redis-py — это привязка, поддерживаемая Django по умолчанию. Также рекомендуется установить пакет hiredis-py.
Чтобы использовать Redis в качестве кэша в Django:
- Установите
BACKENDвdjango.core.cache.backends.redis.RedisCache. - Установите
LOCATIONв URL, указывающий на ваш Redis-экземпляр, используя соответствующую схему. См.redis-pyдокументацию для подробностей доступных схем.
Например, если Redis работает на локальном хосте (127.0.0.1) на порту 6379:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
}
}
Часто Redis-серверы защищены аутентификацией. Чтобы указать имя пользователя и пароль, добавьте их в LOCATION вместе с URL:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": "redis://username:password@127.0.0.1:6379",
}
}
Если у вас установлено несколько Redis-серверов в режиме репликации, вы можете указать серверы, либо как строку, разделенную точкой с запятой или запятыми, либо как список. При использовании нескольких серверов операции записи выполняются на первом сервере (лидер). Операции чтения выполняются на других серверах (реплики), выбранных случайным образом:
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
],
}
}
Кэширование в базе данных
Django может хранить свои кэшированные данные в вашей базе данных. Это наилучшим образом работает, если у вас есть быстрый и хорошо индексированный сервер базы данных.
Чтобы использовать таблицу базы данных в качестве кэша:
- Установите
BACKENDвdjango.core.cache.backends.db.DatabaseCache - Установите
LOCATIONвtablename, имя таблицы базы данных. Это имя может быть любым, если это допустимое имя таблицы, которое ещё не используется в вашей базе данных.
В этом примере имя таблицы кэша — my_cache_table:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.db.DatabaseCache",
"LOCATION": "my_cache_table",
}
}
В отличие от других кэшей, кэш базы данных не поддерживает автоматическое удаление устаревших записей на уровне базы данных. Вместо этого устаревшие записи кэша удаляются каждый раз, когда вызывается add(), set() или touch().
Создание таблицы кэша
Перед использованием кэша базы данных необходимо создать таблицу кэша с помощью этой команды:
python manage.py createcachetable
Это создаёт таблицу в вашей базе данных в нужном формате, ожидаемом системой кэширования базы данных Django. Имя таблицы взято из LOCATION.
Если вы используете несколько кэшей базы данных, createcachetable создаёт по одной таблице для каждого кэша.
Если вы используете несколько баз данных, createcachetable следит за allow_migrate() методом ваших роутеров базы данных (см. ниже).
Как и migrate, createcachetable не будет трогать существующую таблицу. Он будет создавать только недостающие таблицы.
Чтобы вывести SQL-запрос, но не выполнить его, используйте параметр createcachetable --dry-run.
Несколько баз данных
Если вы используете кэширование в базе данных с несколькими базами данных, вам также необходимо настроить инструкции для маршрутизации таблицы кэша базы данных. Для целей маршрутизации таблица кэша базы данных отображается как модель с именем CacheEntry в приложении django_cache. Эта модель не появится в кэше моделей, но её сведения могут использоваться для маршрутизации.
Например, следующий роутер перенаправит все операции чтения из кэша в cache_replica, а все операции записи в cache_primary. Таблица кэша будет синхронизирована только с 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
Если вы не укажете инструкции по маршрутизации для модели кэша базы данных, кэш-бекенд будет использовать базу данных default.
И если вы не используете кэш базы данных, вам не нужно беспокоиться о предоставлении инструкций маршрутизации для модели кэша базы данных.
Кэширование на файловой системе
Файловый бэкенд сериализует и сохраняет каждое значение кэша в отдельный файл. Для использования этого бэкенда установите BACKEND в "django.core.cache.backends.filebased.FileBasedCache" и LOCATION в подходящую директорию. Например, чтобы сохранять данные кэша в /var/tmp/django_cache, используйте это значение:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
"LOCATION": "/var/tmp/django_cache",
}
}
Если вы работаете в Windows, поместите букву диска в начале пути, как показано ниже:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
"LOCATION": "c:/foo/bar",
}
}
Путь к директории должен быть абсолютным — начинаться с корня вашей файловой системы. Неважно, ставите ли вы слеш в конце значения.
Убедитесь, что директория, на которую указывает это значение, существует, доступна для чтения и записи либо что она может быть создана системным пользователем, под которым работает ваш веб-сервер. Продолжая приведенный выше пример, если ваш сервер работает как пользователь apache, убедитесь, что директория /var/tmp/django_cache существует, и она доступна для чтения и записи пользователем apache, или что она может быть создана пользователем apache.
Предупреждение
Когда кэш LOCATION находится внутри MEDIA_ROOT, STATIC_ROOT или STATICFILES_FINDERS, чувствительные данные могут быть раскрыты.
Атака, получившая доступ к файлу кэша, может не только подделать содержимое HTML, которому доверяет ваш сайт, но также удаленно выполнить произвольный код, так как данные сериализуются с использованием pickle.
Предупреждение
Кэширование на файловой системе может замедлиться при хранении большого количества файлов. Если у вас возникнет эта проблема, рассмотрите использование другого механизма кэширования. Вы также можете унаследовать FileBasedCache и улучшить стратегию удаления.
Кэширование в локальной памяти
Это кэш по умолчанию, если в вашем файле настроек не указан другой. Если вам нужны преимущества скорости кэширования в памяти, но у вас нет возможности использовать Memcached, рассмотрите кэш локальной памяти. Этот кэш работает в рамках каждого процесса (см. ниже) и потокобезопасен. Для его использования установите BACKEND в "django.core.cache.backends.locmem.LocMemCache". Например:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
"LOCATION": "unique-snowflake",
}
}
Кэш LOCATION используется для идентификации отдельных хранилищ памяти. Если у вас только один locmem кэш, вы можете опустить LOCATION; однако, если у вас несколько кэшей локальной памяти, вам нужно присвоить имя хотя бы одному из них, чтобы они оставались отдельными.
Кэш использует стратегию удаления наименее недавно используемых элементов (LRU).
Обратите внимание, что каждый процесс будет иметь свою собственную частную копию кэша, что означает отсутствие межпроцессного кэширования. Это также означает, что кэш локальной памяти не является особенно эффективным с точки зрения использования памяти, поэтому он, вероятно, не является хорошим выбором для производственной среды. Он удобен для разработки.
Кэширование-заглушка (для разработки)
Наконец, Django поставляется с кэшем-заглушкой, который фактически ничего не кэширует — он просто реализует интерфейс кэша без выполнения каких-либо действий.
Это полезно, если у вас есть сайт для производства, который использует мощное кэширование в разных местах, но среда разработки/тестирования, где вы не хотите кэширования и не хотите изменять код для специального случая последней среды. Для активации кэширования-заглушки установите BACKEND следующим образом:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.dummy.DummyCache",
}
}
Использование настраиваемого бэкенда кэширования
Хотя Django поставляется с поддержкой нескольких бэкендов кэширования, иногда вам может потребоваться настраиваемый бэкенд кэширования. Для использования внешнего бэкенда кэширования с Django используйте путь импорта Python в качестве BACKEND параметра настройки CACHES, как показано ниже:
CACHES = {
"default": {
"BACKEND": "path.to.backend",
}
}
Если вы создаете собственный бэкенд, вы можете использовать стандартные бэкенды кэширования в качестве эталонных реализаций. Вы найдете код в каталоге django/core/cache/backends/ исходного кода Django.
Примечание: без веской причины, например, из-за хоста, который их не поддерживает, вы должны использовать бэкенды кэширования, включенные в Django. Они были хорошо протестированы и хорошо задокументированы.
Аргументы кэша
Каждый бэкенд кэширования может получать дополнительные аргументы для управления поведением кэширования. Эти аргументы предоставляются как дополнительные ключи в настройках CACHES. Допустимые аргументы следующие:
-
TIMEOUT: Время ожидания по умолчанию в секундах, используемое для кэша. Этот аргумент по умолчанию равен300секундам (5 минут). Вы можете установитьTIMEOUTвNone, чтобы по умолчанию ключи кэша никогда не истекали. Значение0заставляет ключи немедленно истекать (эффективно «не кэшировать»). -
OPTIONS: Любые параметры, которые должны быть переданы бэкенду кэша. Список допустимых параметров будет различаться для каждого бэкенда, и бэкенды кэширования, основанные на сторонней библиотеке, передадут свои параметры напрямую в базовую библиотеку кэширования.Бэкенды кэширования, реализующие собственную стратегию удаления (то есть бэкенды
locmem,filesystemиdatabase), будут учитывать следующие параметры:-
MAX_ENTRIES: Максимальное количество элементов, разрешенных в кэше, прежде чем будут удалены старые значения. Этот аргумент по умолчанию равен300. -
CULL_FREQUENCY: Доля элементов, которые удаляются, когда достигаетсяMAX_ENTRIES. Фактическая доля равна1 / CULL_FREQUENCY, поэтому установитеCULL_FREQUENCYв2, чтобы удалить половину элементов, когда достигаетсяMAX_ENTRIES. Этот аргумент должен быть целым числом и по умолчанию равен3.Значение
0дляCULL_FREQUENCYозначает, что весь кэш будет очищен, когда достигнутMAX_ENTRIES. В некоторых бэкендах (например,database) это делает удаление намного быстрее за счет дополнительных промахов кэша.
Бэкенды Memcached и Redis передают содержимое
OPTIONSв качестве именованных аргументов в конструкторы клиента, что позволяет более точно контролировать поведение клиента. Пример использования приведен ниже. -
-
KEY_PREFIX: Строка, которая будет автоматически добавлена (по умолчанию в префикс) ко всем ключам кэша, используемым сервером Django.Дополнительную информацию см. в документации по кэшу.
-
VERSION: Номер версии по умолчанию для ключей кэша, генерируемых сервером Django.Дополнительную информацию см. в документации по кэшу.
-
KEY_FUNCTIONСтрока, содержащая путь с точкой к функции, определяющей способ составления окончательного ключа кэша из префикса, версии и ключа.Дополнительную информацию см. в документации по кэшу.
В этом примере бэкенд файловой системы настраивается с временем ожидания 60 секунд и максимальной емкостью 1000 элементов:
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
"LOCATION": "/var/tmp/django_cache",
"TIMEOUT": 60,
"OPTIONS": {"MAX_ENTRIES": 1000},
}
}
Вот пример конфигурации для бэкенда на основе pylibmc, который включает двоичный протокол, аутентификацию SASL и режим поведения 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,
},
},
}
}
Вот пример конфигурации для бэкенда на основе pymemcache, который включает пул клиентов (что может повысить производительность за счет поддержания подключений клиентов), обрабатывает ошибки memcache/сети как промахи кэша и устанавливает флаг TCP_NODELAY на сокете соединения:
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,
},
}
}
Вот пример конфигурации для бэкенда на основе redis, который выбирает базу данных 10 (по умолчанию Redis поставляется с 16 логическими базами данных), указывает класс парсера parser class (redis.connection.HiredisParser будет использоваться по умолчанию, если установлен пакет hiredis-py) и устанавливает настраиваемый класс пула подключений connection pool class (redis.ConnectionPool используется по умолчанию):
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"db": "10",
"parser_class": "redis.connection.PythonParser",
"pool_class": "redis.BlockingConnectionPool",
},
}
}
Кэш на уровне сайта
После настройки кэша наиболее простой способ использования кэширования — кэширование всего сайта. Вам нужно добавить 'django.middleware.cache.UpdateCacheMiddleware' и 'django.middleware.cache.FetchFromCacheMiddleware' в настройку MIDDLEWARE, как показано в этом примере:
MIDDLEWARE = [
"django.middleware.cache.UpdateCacheMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.cache.FetchFromCacheMiddleware",
]
Примечание
Нет, это не опечатка: «обновляющий» мидлварь должен быть первым в списке, а «получающий» мидлварь — последним. Подробности немного запутанные, но см. Порядок MIDDLEWARE ниже, если вы хотите узнать все подробности.
Затем добавьте следующие необходимые настройки в файл настроек Django:
-
CACHE_MIDDLEWARE_ALIAS– псевдоним кэша для хранения. -
CACHE_MIDDLEWARE_SECONDS– целое число секунд, в течение которых каждая страница должна храниться в кэше. -
CACHE_MIDDLEWARE_KEY_PREFIX– если кэш используется на нескольких сайтах с одной установкой Django, задайте это значение в имя сайта или какую-либо другую строку, уникальную для данной установки Django, чтобы предотвратить коллизии ключей. Используйте пустую строку, если это не важно.
FetchFromCacheMiddleware кэширует ответы GET и HEAD со статусом 200, если заголовки запроса и ответа позволяют. Ответы на запросы для одного и того же URL с различными параметрами запроса считаются уникальными страницами и кэшируются отдельно. Данный middleware ожидает, что запрос HEAD будет отвечать теми же заголовками ответа, что и соответствующий запрос GET; в этом случае он может возвращать кэшированный ответ GET для запроса HEAD.
Кроме того, UpdateCacheMiddleware автоматически устанавливает несколько заголовков в каждом HttpResponse, которые влияют на кэши последующих уровней:
- Устанавливает заголовок
Expiresсо значением текущей даты/времени плюс определенныеCACHE_MIDDLEWARE_SECONDS. - Устанавливает заголовок
Cache-Control, чтобы указать максимальный срок хранения для страницы – также из настройкиCACHE_MIDDLEWARE_SECONDS.
См. Middleware для получения дополнительной информации о middleware.
Если представление задает собственное время кэширования (т.е. у него есть раздел max-age в заголовке Cache-Control), то страница будет кэшироваться до истечения срока действия, а не CACHE_MIDDLEWARE_SECONDS. Используя декораторы в django.views.decorators.cache, вы можете легко задать срок действия представления (с помощью декоратора cache_control()) или отключить кэширование для представления (с помощью декоратора never_cache()). См. раздел использование других заголовков для получения дополнительной информации об этих декораторах.
Если USE_I18N установлено в True, то сгенерированный ключ кэша будет включать имя активного языка – см. также Как Django определяет предпочтения языка). Это позволяет легко кэшировать многоязычные сайты, не создавая ключ кэша самостоятельно.
Ключи кэша также включают текущую часовую зону, когда USE_TZ установлено в True.
Кэш по представлению
-
django.views.decorators.cache.cache_page(timeout, *, cache=None, key_prefix=None)
Более детальный способ использования системы кэширования – кэширование вывода отдельных представлений. django.views.decorators.cache определяет декоратор cache_page, который автоматически кэширует ответ представления:
from django.views.decorators.cache import cache_page @cache_page(60 * 15) def my_view(request): ...
cache_page принимает один аргумент: время ожидания кэширования в секундах. В примере выше результат представления my_view() будет кэшироваться в течение 15 минут. (Обратите внимание, что мы написали его как 60 * 15 для удобочитаемости. 60 * 15 будет вычислено в 900 – то есть 15 минут умножить на 60 секунд в минуте.)
Время ожидания кэширования, заданное cache_page, имеет приоритет над директивой max-age из заголовка Cache-Control.
Кэш по представлению, как и кэш по сайту, формируется на основе URL. Если несколько URL указывают на одно и то же представление, каждый URL будет кэшироваться отдельно. Продолжая пример my_view, если ваша URL-конфигурация выглядит так:
urlpatterns = [
path("foo/<int:code>/", my_view),
]
то запросы к /foo/1/ и /foo/23/ будут кэшироваться раздельно, как ожидается. Но как только определенный URL (например, /foo/23/) был запрошен, последующие запросы к этому URL будут использовать кэш.
cache_page также может принимать необязательный ключевой аргумент cache, который направляет декоратор использовать определенный кэш (из вашей настройки CACHES) при кэшировании результатов представления. По умолчанию используется кэш default, но вы можете указать любой другой кэш:
@cache_page(60 * 15, cache="special_cache") def my_view(request): ...
Вы также можете переопределить префикс кэша на уровне представления. cache_page принимает необязательный ключевой аргумент key_prefix, который работает так же, как и настройка CACHE_MIDDLEWARE_KEY_PREFIX для middleware. Его можно использовать так:
@cache_page(60 * 15, key_prefix="site1") def my_view(request): ...
Аргументы key_prefix и cache могут быть указаны вместе. Аргумент key_prefix и KEY_PREFIX, указанные в CACHES, будут конкатенированы.
Кроме того, cache_page автоматически устанавливает заголовки Cache-Control и Expires в ответе, которые влияют на кэши последующих уровней.
Указание кэша по представлению в URLconf
Примеры в предыдущем разделе жестко задают кэширование представления, потому что cache_page изменяет функцию my_view на месте. Этот подход связывает ваше представление с системой кэширования, что не идеально по нескольким причинам. Например, вы можете захотеть повторно использовать функции представления на другом, некэшируемом сайте, или вы можете захотеть распределить представления между пользователями, которые могут захотеть использовать их без кэширования. Решение этих проблем заключается в указании кэша по представлению в URLconf, а не рядом с самими функциями представлений.
Вы можете сделать это, обернув функцию представления с помощью cache_page при ссылке на нее в URLconf. Вот старый URLconf из предыдущего примера:
urlpatterns = [
path("foo/<int:code>/", my_view),
]
Вот то же самое, но с my_view, обернутым в cache_page:
from django.views.decorators.cache import cache_page
urlpatterns = [
path("foo/<int:code>/", cache_page(60 * 15)(my_view)),
]
Кэширование фрагментов шаблонов
Если вам нужен еще больший контроль, вы также можете кэшировать фрагменты шаблонов с помощью тега cache. Чтобы предоставить вашему шаблону доступ к этому тегу, поместите {% load cache %} в начало вашего шаблона.
Тег {% cache %} кэширует содержимое блока в течение заданного промежутка времени. Он принимает как минимум два аргумента: время кэширования в секундах и имя фрагмента кэша. Фрагмент кэшируется навсегда, если время кэширования равно None. Имя будет использоваться как есть, не используйте переменную. Например:
{% load cache %}
{% cache 500 sidebar %}
.. sidebar ..
{% endcache %}
Иногда вам может потребоваться кэшировать несколько копий фрагмента в зависимости от некоторых динамических данных, которые появляются внутри фрагмента. Например, вам может потребоваться отдельная кэшированная копия сайдбара, используемого в предыдущем примере, для каждого пользователя вашего сайта. Сделайте это, передав один или несколько дополнительных аргументов, которые могут быть переменными с фильтрами или без них, тегу {% cache %} для уникальной идентификации фрагмента кэша:
{% load cache %}
{% cache 500 sidebar request.user.username %}
.. sidebar for logged in user ..
{% endcache %}
Если USE_I18N установлено в True, кэш middleware по сайту будет учитывать активный язык. Для тега cache можно использовать одну из переменных, специфичных для перевода, доступных в шаблонах, чтобы добиться того же результата:
{% load i18n %}
{% load cache %}
{% get_current_language as LANGUAGE_CODE %}
{% cache 600 welcome LANGUAGE_CODE %}
{% translate "Welcome to example.com" %}
{% endcache %}
Время кэширования может быть шаблоном переменной, если переменная шаблона принимает целочисленное значение. Например, если переменная шаблона my_timeout имеет значение 600, то следующие два примера эквивалентны:
{% cache 600 sidebar %} ... {% endcache %}
{% cache my_timeout sidebar %} ... {% endcache %}
Эта функция полезна для предотвращения повторений в шаблонах. Время кэширования можно задать в переменной в одном месте и использовать это значение повторно.
По умолчанию тег кэша будет пытаться использовать кэш с именем «template_fragments». Если такого кэша не существует, он будет использовать кэш по умолчанию. Вы можете выбрать альтернативный кэш, используя ключевой аргумент using, который должен быть последним аргументом тега.
{% cache 300 local-thing ... using="localcache" %}
Указание имени кэша, который не настроен, считается ошибкой.
-
django.core.cache.utils.make_template_fragment_key(fragment_name, vary_on=None)
Если вам нужно получить ключ кэша, используемый для кэшированного фрагмента, вы можете использовать make_template_fragment_key. fragment_name – это то же самое, что и второй аргумент тега cache; vary_on – это список всех дополнительных аргументов, переданных в тег. Эта функция может быть полезна для аннулирования или перезаписи кэшированного элемента, например:
>>> 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
API кэша низкого уровня
Иногда кэширование всей отрисованной страницы не приносит особой выгоды и даже является избыточным.
Возможно, например, ваш сайт включает представление, результаты которого зависят от нескольких дорогостоящих запросов, результаты которых меняются с разными интервалами. В этом случае не было бы идеальным использовать кэширование всей страницы, которое предлагают стратегии кэширования по сайту или по представлению, потому что вам не хотелось бы кэшировать весь результат (поскольку некоторые данные часто меняются), но вы все равно хотели бы кэшировать результаты, которые редко меняются.
В таких случаях Django предоставляет низкоуровневый API кэширования. Вы можете использовать этот API для хранения объектов в кэше с любой степенью детализации, которую вы хотите. Вы можете кэшировать любой объект Python, который может быть безопасно закодирован с помощью pickling: строки, словари, списки объектов модели и так далее. (Большинство распространенных объектов Python могут быть закодированы с помощью pickling; обратитесь к документации Python для получения дополнительной информации о pickling.)
Доступ к кэшу
-
django.core.cache.caches -
Вы можете получить доступ к кэшам, настроенным в настройке
CACHES, через объект, подобный словарю:django.core.cache.caches. Повторные запросы к одному и тому же псевдониму в одном и том же потоке вернут один и тот же объект.>>> from django.core.cache import caches >>> cache1 = caches["myalias"] >>> cache2 = caches["myalias"] >>> cache1 is cache2 True
Если указанного ключа не существует,
InvalidCacheBackendErrorбудет поднято.Для обеспечения потокобезопасности для каждого потока будет возвращен другой экземпляр кэша бэкенда.
-
django.core.cache.cache -
В качестве сокращения, кэш по умолчанию доступен как
django.core.cache.cache:>>> from django.core.cache import cache
Этот объект эквивалентен
caches['default'].
Основные методы использования
Основные методы использования:
-
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 должен быть str, а value может быть любым закодированным с помощью pickling объектом Python.
Аргумент timeout является необязательным и по умолчанию принимает значение аргумента timeout соответствующего бэкенда в настройке CACHES (объяснено выше). Это количество секунд, в течение которого значение должно храниться в кэше. Передача None для timeout кэширует значение навсегда. timeout значение 0 не будет кэшировать значение.
Если объект не существует в кэше, cache.get() возвращает None:
>>> # Wait 30 seconds for 'my_key' to expire...
>>> cache.get("my_key")
None
Если вам нужно определить, существует ли объект в кэше, и вы сохранили литеральное значение None, используйте sentinel-объект в качестве значения по умолчанию:
>>> 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() может принимать аргумент default. Это определяет, какое значение возвращать, если объект не существует в кэше:
>>> cache.get("my_key", "has expired")
'has expired'
-
cache.add(key, value, timeout=DEFAULT_TIMEOUT, version=None)
Чтобы добавить ключ только в том случае, если он еще не существует, используйте метод add(). Он принимает те же параметры, что и set(), но не будет пытаться обновить кэш, если указанный ключ уже существует:
>>> cache.set("add_key", "Initial value")
>>> cache.add("add_key", "New value")
>>> cache.get("add_key")
'Initial value'
Если вам нужно знать, сохранил ли add() значение в кэше, вы можете проверить возвращаемое значение. Оно вернет True, если значение было сохранено, и False в противном случае.
-
cache.get_or_set(key, default, timeout=DEFAULT_TIMEOUT, version=None)
Если вы хотите получить значение ключа или установить значение, если ключ отсутствует в кэше, существует метод get_or_set(). Он принимает те же параметры, что и get(), но значение по умолчанию устанавливается как новое значение кэша для этого ключа, а не возвращается:
>>> cache.get("my_new_key") # returns None
>>> cache.get_or_set("my_new_key", "my new value", 100)
'my new value'
Вы также можете передать любой вызываемый объект в качестве значения по умолчанию:
>>> 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)
Также существует интерфейс get_many(), который обращается к кэшу только один раз. get_many() возвращает словарь со всеми запрошенными ключами, которые действительно существуют в кэше (и не истекли):
>>> 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)
Для более эффективного задания нескольких значений используйте set_many() для передачи словаря пар ключ-значение:
>>> cache.set_many({"a": 1, "b": 2, "c": 3})
>>> cache.get_many(["a", "b", "c"])
{'a': 1, 'b': 2, 'c': 3}
Как и cache.set(), set_many() принимает необязательный параметр timeout.
В поддерживаемых бэкендах (memcached) set_many() возвращает список ключей, которые не удалось вставить.
-
cache.delete(key, version=None)
Вы можете явно удалить ключи с помощью delete(), чтобы очистить кэш для конкретного объекта:
>>> cache.delete("a")
True
delete() возвращает True, если ключ был успешно удален, и False в противном случае.
-
cache.delete_many(keys, version=None)
Если вы хотите очистить сразу несколько ключей, delete_many() может принимать список ключей для удаления:
>>> cache.delete_many(["a", "b", "c"])
-
cache.clear()
Наконец, если вы хотите удалить все ключи в кэше, используйте cache.clear(). Будьте осторожны, так как clear() удалит все из кэша, а не только ключи, установленные вашей программой.
>>> cache.clear()
-
cache.touch(key, timeout=DEFAULT_TIMEOUT, version=None)
cache.touch() устанавливает новое время истечения для ключа. Например, чтобы обновить ключ, чтобы он истек через 10 секунд:
>>> cache.touch("a", 10)
True
Как и другие методы, аргумент timeout является необязательным и по умолчанию принимает значение опции TIMEOUT соответствующего бэкенда в настройке CACHES.
touch() возвращает True, если ключ был успешно обновлен, и False в противном случае.
-
cache.incr(key, delta=1, version=None)
-
cache.decr(key, delta=1, version=None)
Вы также можете увеличить или уменьшить ключ, который уже существует, используя методы incr() или decr() соответственно. По умолчанию текущее значение кэша будет увеличено или уменьшено на 1. Другие значения для увеличения/уменьшения могут быть указаны путем предоставления аргумента вызову увеличения/уменьшения. ValueError будет поднят, если вы попытаетесь увеличить или уменьшить несуществующий ключ кэша:
>>> cache.set("num", 1)
>>> cache.incr("num")
2
>>> cache.incr("num", 10)
12
>>> cache.decr("num")
11
>>> cache.decr("num", 5)
6
Примечание
Методы incr()/decr() не гарантируют атомарность. В тех бэкендах, которые поддерживают атомарные операции увеличения/уменьшения (в первую очередь, бэкенд memcached), операции увеличения и уменьшения будут атомарными. Однако, если бэкенд не предоставляет операцию увеличения/уменьшения, она будет реализована с использованием двухэтапного получения/обновления.
-
cache.close()
Вы можете закрыть соединение с кэшем с помощью close(), если это реализовано кэшем бэкенда.
>>> cache.close()
Примечание
Для кэшей, которые не реализуют методы close, это no-op.
Примечание
Асинхронные варианты базовых методов имеют префикс a, например, cache.aadd() или cache.adelete_many(). Подробнее см. Асинхронная поддержка.
Префикс ключа кэша
Если вы используете экземпляр кэша на нескольких серверах или в различных средах (производство/разработка), возможно, что данные, кэшированные одним сервером, будут использоваться другим сервером. Если формат кэшированных данных различается между серверами, это может привести к проблемам, которые сложно диагностировать.
Чтобы избежать этого, Django предоставляет возможность префикса всех ключей кэша, используемых сервером. Когда конкретный ключ кэша сохраняется или извлекается, Django автоматически префикс к ключу кэша значением настройки кэша KEY_PREFIX.
Убедившись, что каждый экземпляр Django имеет уникальный префикс KEY_PREFIX, вы можете гарантировать отсутствие коллизий в значениях кэша.
Версионирование кэша
При изменении кода, использующего кэшированные значения, вам может потребоваться очистка существующих кэшированных значений. Самый простой способ сделать это — очистить весь кэш, но это может привести к потере валидных и полезных значений кэша.
Django предоставляет лучший способ для таргетирования отдельных значений кэша. Система кэширования Django имеет глобальный идентификатор версии, указанный в настройке кэша VERSION. Значение этой настройки автоматически комбинируется с префиксом кэша и предоставленным пользователем ключом кэша для получения конечного ключа кэша.
По умолчанию любой запрос ключа автоматически включает версию глобального ключа кэша. Однако все базовые функции кэша включают аргумент version, поэтому вы можете указать конкретную версию ключа кэша для установки или получения. Например:
>>> # 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!'
Версия определенного ключа может быть увеличена или уменьшена с помощью методов incr_version() и decr_version(). Это позволяет обновлять определенные ключи до новой версии, не затрагивая другие. Продолжая наш предыдущий пример:
>>> # 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!'
Преобразование ключа кэша
Как описано в предыдущих двух разделах, предоставленный пользователем ключ кэша не используется напрямую — он комбинируется с префиксом кэша и версией ключа для получения конечного ключа кэша. По умолчанию три части соединяются двоеточием, чтобы получить конечную строку:
def make_key(key, key_prefix, version):
return "%s:%s:%s" % (key_prefix, version, key)
Если вы хотите комбинировать части различными способами или применить другую обработку к итоговому ключу (например, вычислить хэш-хеш отдельных частей ключа), вы можете предоставить пользовательскую функцию ключа.
Настройка кэша KEY_FUNCTION указывает путь с точками к функции, соответствующей прототипу make_key() выше. Если она предоставлена, эта пользовательская функция ключа будет использоваться вместо функции по умолчанию для объединения ключей.
Предупреждения о ключах кэша
Memcached, наиболее часто используемый кэширующий бэкенд в рабочей среде, не допускает ключей кэша длиннее 250 символов или содержащих пробелы или управляющие символы, и использование таких ключей вызовет исключение. Для поощрения переноса кода, совместимого с кэшированием, и минимизации неприятных сюрпризов другие встроенные бэкэнды кэша выдают предупреждение (django.core.cache.backends.base.CacheKeyWarning), если используется ключ, который может вызвать ошибку в memcached.
Если вы используете бэкенд рабочей среды, который может принимать более широкий спектр ключей (пользовательский бэкенд или один из встроенных бэкэндов, не использующих memcached), и хотите использовать этот более широкий спектр без предупреждений, вы можете отключить CacheKeyWarning с помощью этого кода в модуле management одного из ваших INSTALLED_APPS:
import warnings
from django.core.cache import CacheKeyWarning
warnings.simplefilter("ignore", CacheKeyWarning)
Если вы хотите вместо этого предоставить пользовательскую логику проверки ключей для одного из встроенных бэкэндов, вы можете унаследовать его, переопределить только метод validate_key и следовать инструкциям по использованию пользовательского кэширующего бэкенда. Например, чтобы сделать это для бэкенда locmem, поместите этот код в модуль:
from django.core.cache.backends.locmem import LocMemCache
class CustomLocMemCache(LocMemCache):
def validate_key(self, key):
"""Custom validation, raising exceptions or warnings as needed."""
...
…и используйте путь с точками в Python для этого класса в разделе BACKEND вашей настройки CACHES.
Поддержка асинхронных операций
Django разрабатывает поддержку асинхронных кэширующих бэкэндов, но пока не поддерживает асинхронное кэширование. Это будет добавлено в будущих версиях.
django.core.cache.backends.base.BaseCache имеет асинхронные варианты всех основных методов. По соглашению, асинхронные версии всех методов имеют префикс a. По умолчанию аргументы для обоих вариантов одинаковы:
>>> await cache.aset("num", 1)
>>> await cache.ahas_key("num")
True
Кэши нижнего уровня
До сих пор этот документ фокусировался на кэшировании ваших данных. Но существует и другой тип кэширования, относящийся к разработке веб-приложений: кэширование, выполняемое «нижележащими» кэшами. Это системы, которые кэшируют страницы для пользователей еще до того, как запрос достигнет вашего веб-сайта.
Вот несколько примеров кэшей нижнего уровня:
- При использовании HTTP ваш ISP может кэшировать определенные страницы, поэтому если вы запросили страницу с
http://example.com/, ваш ISP отправит вам страницу без необходимости прямого доступа к example.com. Удаленные разработчики example.com не имеют знания об этом кэшировании; ISP находится между example.com и вашим веб-браузером, обрабатывая все кэширование прозрачно. Такое кэширование невозможно при HTTPS, так как это было бы атаки «человек посередине». - Ваш веб-сайт Django может находиться за прокси-кэшем, таким как Squid Web Proxy Cache (http://www.squid-cache.org/), который кэширует страницы для повышения производительности. В этом случае каждый запрос сначала обрабатывается прокси, и он передается вашему приложению только при необходимости.
- Ваш веб-браузер также кэширует страницы. Если веб-страница отправляет соответствующие заголовки, ваш браузер будет использовать локальную кэшированную копию для последующих запросов на эту страницу, даже не обращаясь к веб-странице снова, чтобы узнать, изменилась ли она.
Кэширование нижнего уровня - это хорошая прибавка к эффективности, но существует опасность: содержимое многих веб-страниц отличается в зависимости от аутентификации и множества других переменных, и системы кэширования, которые слепо сохраняют страницы исключительно на основе URL-адресов, могут раскрыть неверные или конфиденциальные данные последующим посетителям этих страниц.
Например, если вы работаете с веб-системой электронной почты, то содержимое страницы «входящие» зависит от пользователя, который авторизован. Если ISP слепо кэширует ваш сайт, то у первого пользователя, который вошел через этот ISP, на страницу его входящих будет кэширован для последующих посетителей сайта. Это не круто.
К счастью, HTTP предоставляет решение этой проблемы. Существует ряд заголовков HTTP, чтобы указать кэшам нижнего уровня изменять содержимое кэша в зависимости от указанных переменных и сообщать механизмам кэширования о том, чтобы не кэшировать определенные страницы. Мы рассмотрим некоторые из этих заголовков в следующих разделах.
Использование заголовков Vary
Заголовок Vary определяет, какие заголовки запроса механизм кэширования должен учитывать при построении ключа кэша. Например, если содержимое веб-страницы зависит от предпочтений языка пользователя, то страница считается «зависимой от языка».
По умолчанию система кэширования Django создает свои ключи кэша, используя запрашиваемый полностью квалифицированный URL-адрес, например, "https://www.example.com/stories/2005/?order_by=author". Это означает, что каждый запрос к этому URL-адресу будет использовать ту же кэшированную версию, независимо от различий в пользовательских агентах, таких как куки или языковые предпочтения. Однако, если эта страница генерирует разное содержимое в зависимости от различий в заголовках запроса, таких как куки, язык или пользовательский агент, вам необходимо использовать заголовок Vary, чтобы указать механизмам кэширования, что вывод страницы зависит от этих факторов.
Для этого в Django используйте удобный декоратор представления django.views.decorators.vary.vary_on_headers(), например:
from django.views.decorators.vary import vary_on_headers
@vary_on_headers("User-Agent")
def my_view(request): ...
В этом случае механизм кэширования (такой как собственный кэширующий посредник Django) будет кэшировать отдельную версию страницы для каждого уникального пользовательского агента.
Преимущества использования декоратора vary_on_headers вместо ручного задания заголовка Vary (например, с помощью response.headers['Vary'] =
'user-agent') заключается в том, что декоратор добавляет к заголовку Vary (который может уже существовать), а не устанавливает его с нуля и потенциально перезаписывает все, что уже было в нем.
Вы можете передать несколько заголовков в vary_on_headers():
@vary_on_headers("User-Agent", "Cookie")
def my_view(request): ...
Это сообщает кэшам нижнего уровня изменять кэш на основе обоих факторов, что означает, что каждая комбинация пользовательского агента и куки получит своё значение кэша. Например, запрос с пользовательским агентом Mozilla и значением куки foo=bar будет считаться отличающимся от запроса с пользовательским агентом Mozilla и значением куки foo=ham.
Поскольку изменение на основе куки столь распространено, существует декоратор django.views.decorators.vary.vary_on_cookie(). Эти два представления эквивалентны:
@vary_on_cookie
def my_view(request): ...
@vary_on_headers("Cookie")
def my_view(request): ...
Заголовки, которые вы передаете в vary_on_headers, не чувствительны к регистру; "User-Agent" это то же самое, что и "user-agent".
Вы также можете использовать вспомогательную функцию django.utils.cache.patch_vary_headers() напрямую. Эта функция устанавливает или добавляет к заголовку Vary header. Например:
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 принимает экземпляр HttpResponse в качестве первого аргумента и список/кортеж имён заголовков (нечувствительных к регистру) в качестве второго аргумента.
Дополнительную информацию о заголовках Vary см. в официальной спецификации Vary.
Управление кэшем с использованием других заголовков
Другие проблемы с кэшированием связаны с конфиденциальностью данных и вопросом, где данные должны храниться в каскаде кэшей.
Пользователь обычно сталкивается с двумя видами кэшей: кэш своего браузера (частный кэш) и кэш своего провайдера (общедоступный кэш). Общедоступный кэш используется несколькими пользователями и управляется кем-то другим. Это создает проблемы с конфиденциальными данными — вы не хотите, чтобы, например, номер вашего банковского счета хранился в общедоступном кэше. Поэтому веб-приложениям нужна возможность указывать кэшам, какие данные являются конфиденциальными, а какие — общедоступными.
Решение заключается в указании того, что кэш страницы должен быть «частным». Для этого в Django используется декоратор представления cache_control(). Пример:
from django.views.decorators.cache import cache_control @cache_control(private=True) def my_view(request): ...
Этот декоратор позаботится об отправке соответствующего заголовка HTTP за кулисами.
Обратите внимание, что настройки кэширования «частный» и «общедоступный» взаимоисключающие. Декоратор гарантирует, что директива «общедоступный» удаляется, если должна быть установлена «частный» (и наоборот). Пример использования этих двух директив — веб-сайт блога, который предоставляет как частные, так и общедоступные записи. Общедоступные записи могут быть кэшированы в любом общем кэше. Следующий код использует patch_cache_control(), ручное изменение заголовка управления кэшем (он используется внутренне декоратором 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
Вы можете управлять кэшами нижнего уровня и другими способами (см. RFC 9111 для получения подробной информации о кэшировании HTTP). Например, даже если вы не используете фреймворк кэширования Django на стороне сервера, вы по-прежнему можете сообщить клиентам, чтобы они кэшировали представление на определенное время с помощью директивы max-age:
from django.views.decorators.cache import cache_control @cache_control(max_age=3600) def my_view(request): ...
(Если вы используете кэширующий посредник, он уже устанавливает max-age со значением настройки CACHE_MIDDLEWARE_SECONDS. В этом случае пользовательский max_age от декоратора cache_control() будет иметь приоритет, и значения заголовков будут объединены корректно.)
Любая допустимая директива ответа Cache-Control допустима в cache_control(). Вот несколько дополнительных примеров:
no_transform=Truemust_revalidate=Truestale_while_revalidate=num_secondsno_cache=True
Полный список известных директив можно найти в реестре IANA IANA registry (обратите внимание, что не все из них применяются к ответам).
Если вы хотите использовать заголовки для полного отключения кэширования, never_cache() — это декоратор представления, который добавляет заголовки, чтобы гарантировать, что браузеры или другие кэши не будут кэшировать ответ. Пример:
from django.views.decorators.cache import never_cache @never_cache def myview(request): ...
Порядок MIDDLEWARE
Если вы используете кэширующий промежуточный программный модуль, важно правильно расположить каждый его компонент в настройках MIDDLEWARE. Это необходимо, так как кэширующему промежуточному программному модулю нужно знать, какие заголовки использовать для задания кэширования данных. Промежуточный программный модуль всегда добавляет что-то к заголовку ответа Vary, когда это возможно.
UpdateCacheMiddleware выполняется во время фазы ответа, где промежуточные программные модули выполняются в обратном порядке, поэтому элемент в верхней части списка выполняется последним во время фазы ответа. Таким образом, вам нужно убедиться, что UpdateCacheMiddleware находится перед любым другим промежуточным программным модулем, который может добавить что-то к заголовку Vary. Следующие модули промежуточного программного обеспечения делают это:
-
SessionMiddlewareдобавляетCookie -
GZipMiddlewareдобавляетAccept-Encoding -
LocaleMiddlewareдобавляетAccept-Language
FetchFromCacheMiddleware, с другой стороны, выполняется во время фазы запроса, где промежуточные программные модули применяются от первого к последнему, поэтому элемент в верхней части списка выполняется первым во время фазы запроса. FetchFromCacheMiddleware также должен выполняться после того, как другие промежуточные программные модули обновили заголовок Vary, поэтому FetchFromCacheMiddleware должен стоять после любого элемента, который это делает.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/5.0/topics/cache/