Spec-Zone.ru › Django 4.0

Система кэширования Django

Основная проблема динамических веб-сайтов заключается в том, что они динамичны. Каждый раз, когда пользователь запрашивает страницу, веб-сервер выполняет множество вычислений — от запросов к базе данных до рендеринга шаблонов и бизнес-логики — чтобы создать страницу, которую видит посетитель сайта. Это значительно дороже с точки зрения накладных расходов на обработку, чем стандартное расположение сервера, который просто считывает файл из файловой системы.

Для большинства веб-приложений эти накладные расходы не являются проблемой. Большинство веб-приложений — это сайты малого и среднего размера с невысокой посещаемостью. Но для сайтов со средней и высокой посещаемостью крайне важно сократить все возможные накладные расходы.

Здесь на помощь приходит кэширование.

Кэширование — это сохранение результата дорогостоящего вычисления, чтобы не выполнять его повторно в следующий раз. Вот псевдокод, объясняющий, как это можно применить к динамически генерируемой веб-странице:

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 — путь к файлу 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',
        ]
    }
}

Последний момент относительно Memcached заключается в том, что кэширование на основе памяти имеет недостаток: поскольку кэшированные данные хранятся в памяти, они будут потеряны при сбое сервера. Очевидно, что память не предназначена для постоянного хранения данных, поэтому не полагайтесь на кэширование на основе памяти как на единственный способ хранения данных. Безусловно, ни один из Django-кэшей не должен использоваться для постоянного хранения — они все предназначены для кэширования, а не хранения — но мы указываем на это здесь, потому что кэширование на основе памяти особенно временное.

Изменено в Django 3.2:

Бэкенд PyMemcacheCache был добавлен.

Устарело начиная с версии 3.2: Бэкенд MemcachedCache устарел, так как python-memcached имеет некоторые проблемы и, похоже, не поддерживается. Используйте PyMemcacheCache или PyLibMCCache вместо него.

Redis

Новое в Django 4.0.

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. Эта модель не отобразится в кэше моделей, но подробности модели могут быть использованы для роутинга.

END_OF_DOCUMENT_MARKER

Например, следующий роутер будет направлять все операции чтения кэша в 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).

Обратите внимание, что каждый процесс будет иметь свою собственную частную экземпляр кэша, что означает невозможность кросс-процессного кэширования. Это также означает, что кэш локальной памяти не является особо эффективным с точки зрения памяти, поэтому он, вероятно, не является хорошим выбором для производственной среды. Он удобен для разработки.

Кэширование макетов (для разработки)

Наконец, 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 «обновления» должен стоять первым в списке, а middleware «получения» — последним. Подробности немного неясные, но см. Порядок 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.

См. Мидлвары для получения дополнительной информации о мидлварях.

Если представление устанавливает собственное время кэширования (т. е. у него есть раздел 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()

Более точный способ использования системы кэширования — кэширование вывода отдельных представлений. 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. Вот старый 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 %} кэширует содержимое блока в течение заданного времени. Он принимает по крайней мере два аргумента: время кэширования в секундах и имя фрагмента кэша. Фрагмент кэшируется навсегда, если timeout равен 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

MemcachedCache

Из-за ограничения python-memcached, невозможно отличить сохранённое None значение от пропущенного запроса в кэше, обозначенного возвращаемым значением None в устаревшем бэкенде MemcachedCache.

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(). Смотрите Поддержка асинхронных операций для получения дополнительной информации.

Изменено в Django 4.0:

Асинхронные варианты методов были добавлены в 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 4.0.

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). Например, даже если вы не используете фреймворк кэширования Django на стороне сервера, вы всё равно можете указать клиентам кэшировать представление на определённое время с помощью директивы max-age:

from django.views.decorators.cache import cache_control

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

(Если вы используете кэширующий middleware, он уже устанавливает max-age со значением параметра CACHE_MIDDLEWARE_SECONDS. В этом случае настраиваемое значение max_age из декоратора cache_control() будет иметь приоритет, и значения заголовков будут объединены корректно.)

Любой допустимый Cache-Control директива ответа допустима в cache_control(). Вот несколько примеров:

  • no_transform=True
  • must_revalidate=True
  • stale_while_revalidate=num_seconds
  • no_cache=True

Полный список известных директив можно найти в реестре IANA (обратите внимание, что не все из них применяются к ответам).

Если вы хотите использовать заголовки для отключения кэширования вообще, never_cache() — декоратор представления, который добавляет заголовки, чтобы гарантировать, что ответ не будет кэшироваться браузерами или другими кэшами. Пример:

from django.views.decorators.cache import never_cache

@never_cache
def myview(request):
    ...

Порядок MIDDLEWARE

Если вы используете кэширующий middleware, важно поместить каждую его часть в нужное место в настройке MIDDLEWARE. Это необходимо, потому что кэширующий middleware должен знать, по каким заголовкам изменять хранилище кэша. Middleware всегда добавляет что-то в заголовок ответа Vary когда это возможно.

UpdateCacheMiddleware выполняется во время фазы ответа, где middleware выполняется в обратном порядке, поэтому элемент в начале списка выполняется в последнюю очередь во время фазы ответа. Таким образом, вам необходимо убедиться, что UpdateCacheMiddleware находится перед любым другим middleware, который может добавить что-то в заголовок Vary. Следующие модули middleware делают это:

  • SessionMiddleware добавляет Cookie
  • GZipMiddleware добавляет Accept-Encoding
  • LocaleMiddleware добавляет Accept-Language

FetchFromCacheMiddleware, с другой стороны, выполняется во время фазы запроса, где middleware применяется от первого к последнему, поэтому элемент в начале списка выполняется в первую очередь во время фазы запроса. FetchFromCacheMiddleware также должен выполняться после того, как другой middleware обновит заголовок Vary, поэтому FetchFromCacheMiddleware должен находиться после любого элемента, который это делает.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/4.0/topics/cache/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API