Система кэширования 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 вам потребуется установить плагин для работы с ним. Существует несколько доступных 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— путь к файлу сокета Unix для Memcached.
В этом примере Memcached работает на локальном хосте (127.0.0.1) на порту 11211, используя плагин pymemcache:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': '127.0.0.1:11211',
}
}
В этом примере Memcached доступен через локальный файл сокета Unix /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',
]
}
}
По умолчанию, backend PyMemcacheCache устанавливает следующие параметры (вы можете переопределить их в OPTIONS):
'OPTIONS': {
'allow_unicode_keys': True,
'default_noreply': False,
'serde': pymemcache.serde.pickle_serde,
}
Последний момент о Memcached: кэширование на основе памяти имеет недостаток: так как кэшированные данные хранятся в памяти, они будут потеряны, если ваш сервер выйдет из строя. Очевидно, что память не предназначена для постоянного хранения данных, поэтому не полагайтесь на кэширование на основе памяти как на единственный способ хранения данных. Безусловно, ни один из кэширующих backends Django не должен использоваться для постоянного хранения — они все предназначены для решения задач кэширования, а не хранения — но мы указываем на это здесь, потому что кэширование на основе памяти особенно временное.
Redis
Redis — это база данных в оперативной памяти, которую можно использовать для кэширования. Для начала вам понадобится запущенный локально или на удалённой машине сервер Redis.
После настройки сервера Redis вам потребуется установить Python-плагины для работы с Redis. redis-py — это плагин, поддерживаемый Django напрямую. Также рекомендуется установить дополнительный пакет hiredis-py.
Для использования Redis в качестве кэширующего backenda 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 может хранить кэшированные данные в вашей базе данных. Это лучше всего работает, если у вас есть быстрый и хорошо индексированный сервер базы данных.
Для использования таблицы базы данных в качестве кэширующего backenda:
- Установите
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',
}
}
В отличие от других кэширующих backends, кэш базы данных не поддерживает автоматическое удаление истекших записей на уровне базы данных. Вместо этого истекшие записи кэша удаляются каждый раз, когда вызывается 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.
Кеширование в локальной памяти
Это кеш по умолчанию, если другой не указан в вашем файле настроек. Если вам нужны преимущества скорости кеширования в памяти, но у вас нет возможности запускать Memcached, рассмотрите бэкенд кеширования локальной памяти. Этот кеш предназначен для одного процесса (см. ниже) и потокобезопасен. Для его использования установите BACKEND на "django.core.cache.backends.locmem.LocMemCache". Например:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
Кеш LOCATION используется для идентификации отдельных хранилищ памяти. Если у вас только один locmem кеш, вы можете опустить LOCATION; однако, если у вас более одного кэша локальной памяти, вам нужно будет назначить имя хотя бы одному из них, чтобы они оставались отдельными.
Кеш использует стратегию удаления наименее часто используемых элементов (LRU).
Обратите внимание, что каждый процесс будет иметь свой собственный частный экземпляр кеша, что означает невозможность кэширования между процессами. Это также означает, что кеш локальной памяти не является особенно эффективным с точки зрения использования памяти, поэтому он, вероятно, не является хорошим выбором для производственных сред. Он удобен для разработки.
Кеширование «Dummy» (для разработки)
Наконец, Django поставляется с «фиктивным» кэшем, который фактически не кэширует — он просто реализует интерфейс кэша без выполнения каких-либо действий.
Это полезно, если у вас есть сайт в производстве, который использует мощное кэширование в разных местах, но среда разработки/тестирования, где вы не хотите кэшировать и не хотите изменять свой код для специальных случаев последней. Чтобы активировать кеширование «Dummy», установите 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 «update» должен стоять первым в списке, а middleware «fetch» — последним. Подробности немного запутанные, но см. Порядок MIDDLEWARE ниже, если вы хотите узнать все подробности.
Затем добавьте следующие необходимые настройки в свой файл настроек Django:
-
CACHE_MIDDLEWARE_ALIAS– Кэшируемый псевдоним для хранения. -
CACHE_MIDDLEWARE_SECONDS– Количество секунд кэширования каждой страницы. -
CACHE_MIDDLEWARE_KEY_PREFIX– Если кэш используется на нескольких сайтах с одной установкой Django, установите это значение в имя сайта или другую уникальную строку для этого экземпляра Django, чтобы предотвратить столкновения ключей. Используйте пустую строку, если это не важно.
FetchFromCacheMiddleware кэширует ответы GET и HEAD со статусом 200, где заголовки запроса и ответа это позволяют. Ответы на запросы одного и того же URL с различными параметрами запроса считаются уникальными страницами и кэшируются отдельно. Этот мидлварь ожидает, что запрос HEAD будет отвечен теми же заголовками ответа, что и соответствующий запрос GET; в этом случае он может вернуть кэшированный ответ GET для запроса HEAD.
Кроме того, UpdateCacheMiddleware автоматически устанавливает несколько заголовков в каждом HttpResponse, которые влияют на кэши нижестоящего уровня:
- Устанавливает заголовок
Expiresна текущую дату/время плюс заданное значениеCACHE_MIDDLEWARE_SECONDS. - Устанавливает заголовок
Cache-Controlдля указания максимального времени существования страницы — опять же, из настройкиCACHE_MIDDLEWARE_SECONDS.
См. 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 для мидлваера. Он может быть использован так:
@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. Вот предыдущая конфигурация URL:
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, кэш мидлваера по сайту будет учитывать активный язык. Для тега 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, который может быть безопасно закодирован: строки, словари, списки объектов модели и так далее. (Большинство обычных объектов Python могут быть закодированы; обратитесь к документации Python для получения дополнительной информации о кодировании.)
Доступ к кэшу
-
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 может быть любым закодируемым объектом 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 = 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 это недействующая операция.
Примечание
Асинхронные варианты базовых методов имеют префикс a, например cache.aadd() или cache.adelete_many(). Подробнее см. Поддержка асинхронных операций.
Асинхронные варианты методов были добавлены в BaseCache.
Префикс ключей кэша
Если вы используете один экземпляр кэша на нескольких серверах или в разных средах (производство/разработка), данные, кэшированные одним сервером, могут быть использованы другим сервером. Если формат кэшированных данных различается между серверами, это может привести к трудно диагностируемым проблемам.
Чтобы предотвратить это, 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 будет использовать ту же кэшированную версию, независимо от различий в пользовательском агенте, таких как файлы cookie или предпочтения языка. Однако, если эта страница генерирует разное содержимое на основе каких-либо различий в заголовках запроса, таких как файлы cookie, язык или пользовательский агент, вам необходимо использовать заголовок 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):
...
Это указывает кэшам нижнего уровня варьироваться по обоим параметрам, что означает, что каждой комбинации пользовательского агента и файла cookie будет присвоен свой собственный кэш-значение. Например, запрос с пользовательским агентом Mozilla и значением файла cookie foo=bar будет считаться отличным от запроса с пользовательским агентом Mozilla и значением файла cookie foo=ham.
Поскольку изменение по файлу cookie встречается так часто, существует декоратор 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 7234 для получения подробной информации о кэшировании 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 (обратите внимание, что не все из них применяются к ответам).
Если вы хотите использовать заголовки для отключения кеширования вообще, 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/4.1/topics/cache/