Spec-Zone.ru › Django 5.1

Система кеширования 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 вам потребуется установить его привязку. Существует несколько доступных 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",
        ],
    }
}

По умолчанию, кеш PyMemcacheCache устанавливает следующие параметры (вы можете переопределить их в OPTIONS):

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

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

Redis

Redis — это баз данных в оперативной памяти, которая может использоваться для кеширования. Для начала вам понадобится работающий сервер Redis, либо локально, либо на удаленной машине.

После настройки сервера Redis вам потребуется установить Python-привязки для Redis. redis-py — это привязка, поддерживаемая Django в виде модуля. Также рекомендуется установить пакет hiredis-py.

Чтобы использовать Redis в качестве кеша в Django:

  • Установите BACKEND на django.core.cache.backends.redis.RedisCache.
  • Установите LOCATION на URL, указывающий на ваш экземпляр Redis, используя соответствующую схему. См. документацию по redis-py для подробностей о доступных схемах.

Например, если Redis запущен на локальном хосте (127.0.0.1) на порту 6379:

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

Часто серверы Redis защищены аутентификацией. Для обеспечения имени пользователя и пароля добавьте их в LOCATION вместе с URL:

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

Если у вас несколько серверов Redis настроены в режиме репликации, вы можете указать серверы, используя разделители точка с запятой или запятые, или в виде списка. При использовании нескольких серверов операции записи выполняются на первом сервере (лидере). Операции чтения выполняются на других серверах (репликах), выбираемых случайным образом:

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

Кеширование в базе данных

Django может хранить кешированные данные в вашей базе данных. Это работает лучше всего, если у вас есть быстрый и хорошо индексированный сервер базы данных.

Чтобы использовать таблицу базы данных в качестве кеша:

  • Установите BACKEND на django.core.cache.backends.db.DatabaseCache
  • Установите LOCATION на tablename, имя таблицы базы данных. Это имя может быть любым, если это допустимое имя таблицы, которое еще не используется в вашей базе данных.

В этом примере имя таблицы кеша — my_cache_table:

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

В отличие от других кешей, кеш базы данных не поддерживает автоматическое удаление истекших записей на уровне базы данных. Вместо этого истекшие записи кеша удаляются каждый раз, когда вызывается add(), set(), или touch().

Создание таблицы кеша

Перед использованием кеша базы данных необходимо создать таблицу кеша с помощью этой команды:

python manage.py createcachetable

Это создает таблицу в вашей базе данных в правильном формате, который ожидает система кеширования базы данных Django. Название таблицы взято из LOCATION.

Если вы используете несколько кешей базы данных, createcachetable создаст одну таблицу для каждого кеша.

Если вы используете несколько баз данных, createcachetable соблюдает метод маршрутизации allow_migrate() вашей базы данных (см. ниже).

Как и migrate, createcachetable не будет трогать существующую таблицу. Он будет создавать только недостающие таблицы.

Чтобы вывести SQL, который будет запущен, а не запускать его, используйте опцию createcachetable --dry-run.

Несколько баз данных

Если вы используете кеширование в базе данных с несколькими базами данных, вам также потребуется настроить инструкции по маршрутизации для таблицы кеша вашей базы данных. Для целей маршрутизации таблица кеша базы данных отображается как модель с именем CacheEntry, в приложении с именем django_cache. Эта модель не будет отображаться в кеше моделей, но сведения о модели могут использоваться для целей маршрутизации.

Например, следующий маршрутизатор будет направлять все операции чтения кеша в cache_replica, а все операции записи в cache_primary. Таблица кеша будет синхронизирована только с cache_primary:

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

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

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

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

Если вы не зададите направления маршрутизации для модели кеша базы данных, система кеширования базы данных будет использовать базу данных default.

И если вы не используете кеширование в базе данных, вам не нужно беспокоиться о предоставлении инструкций по маршрутизации для модели кеша базы данных.

Кеширование на файловой системе

Файловый бэкенд сериализует и сохраняет каждое значение кэша в отдельный файл. Чтобы использовать этот бэкенд, установите BACKEND в "django.core.cache.backends.filebased.FileBasedCache" и LOCATION в подходящую директорию. Например, чтобы хранить кэшированные данные в /var/tmp/django_cache, используйте такую настройку:

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

Если вы работаете в Windows, укажите букву диска в начале пути, как показано ниже:

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

Путь к директории должен быть абсолютным — начинаться с корня вашей файловой системы. Не имеет значения, ставите ли вы слеш в конце настройки.

Убедитесь, что директория, указанная в этой настройке, существует, доступна для чтения и записи, или что она может быть создана системным пользователем, под которым работает ваш веб-сервер. Продолжая пример выше, если ваш сервер работает как пользователь apache, убедитесь, что директория /var/tmp/django_cache существует, доступна для чтения и записи пользователем apache, или что она может быть создана пользователем apache.

Предупреждение

Когда кэш LOCATION находится внутри MEDIA_ROOT, STATIC_ROOT или STATICFILES_FINDERS, конфиденциальные данные могут быть раскрыты.

Злоумышленник, получивший доступ к файлу кэша, не только может подделать содержимое HTML, которому доверяет ваш сайт, но и может удаленно выполнить произвольный код, так как данные сериализуются с использованием pickle.

Предупреждение

Файловый кэширование может замедлиться при хранении большого числа файлов. Если вы столкнетесь с этой проблемой, рассмотрите возможность использования другого механизма кэширования. Вы также можете расширить класс FileBasedCache и улучшить стратегию удаления устаревших данных.

Кэширование в оперативной памяти

Это кэш по умолчанию, если другой не указан в файле настроек. Если вам нужны преимущества скорости кэширования в оперативной памяти, но у вас нет возможности использовать Memcached, рассмотрите локальный кэш оперативной памяти. Этот кэш принадлежит одному процессу (см. ниже) и является потокобезопасным. Чтобы использовать его, установите BACKEND в "django.core.cache.backends.locmem.LocMemCache". Например:

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

Кэш LOCATION используется для идентификации отдельных хранилищ в памяти. Если у вас только один locmem кэш, вы можете опустить LOCATION; однако, если у вас более одного локального кэша памяти, вам нужно будет присвоить имя хотя бы одному из них, чтобы сохранить их отдельные.

Кэш использует стратегию удаления наименее используемых элементов (LRU).

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

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

Наконец, Django поставляется с кэшем «dummy», который фактически не кэширует — он просто реализует интерфейс кэша без выполнения каких-либо действий.

Это полезно, если у вас есть сайт в рабочей среде, использующий мощные механизмы кэширования в различных местах, но в среде разработки/тестирования вы не хотите кэшировать и не хотите изменять свой код для обработки последнего случая. Чтобы активировать кэширование 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 логическими базами данных) и устанавливает пользовательский класс пула подключений (redis.ConnectionPool используется по умолчанию):

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

Кэш для сайта

После настройки кэша, самый простой способ его использования — кэширование всего сайта. Вам нужно добавить '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 с различными параметрами запроса считаются уникальными страницами и кэшируются отдельно. Этот middleware ожидает, что запрос HEAD будет отвечен теми же заголовками ответа, что и соответствующий запрос GET; в этом случае он может вернуть кэшированный ответ GET для запроса HEAD.

Кроме того, UpdateCacheMiddleware автоматически устанавливает несколько заголовков в каждом HttpResponse, которые влияют на кэши нижнего уровня:

  • Устанавливает заголовок Expires на текущую дату/время плюс определённые CACHE_MIDDLEWARE_SECONDS.
  • Устанавливает заголовок Cache-Control для указания максимального срока хранения страницы – опять же, из CACHE_MIDDLEWARE_SECONDS настройки.

См. Middleware для получения дополнительной информации о middleware.

Если представление устанавливает собственное время истечения кэша (т.е. оно имеет раздел max-age в своём заголовке Cache-Control) то страница будет кэшироваться до времени истечения, а не CACHE_MIDDLEWARE_SECONDS. Используя декораторы в django.views.decorators.cache, вы можете легко установить время истечения представления (используя декоратор cache_control()) или отключить кэширование для представления (используя декоратор never_cache()). Смотрите раздел использование других заголовков для получения дополнительной информации об этих декораторах.

Если USE_I18N установлено в True, то сгенерированный ключ кэша будет включать имя активного языка – см. также Как Django обнаруживает предпочтение языка). Это позволяет легко кэшировать многоязычные сайты, не создавая ключ кэша самостоятельно.

Ключи кэша также включают текущую часовую зону при установке USE_TZ в True.

Кэш на уровне представления

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

Более точный способ использования системы кэширования – кэширование выходных данных отдельных представлений. django.views.decorators.cache определяет декоратор cache_page, который автоматически кэширует ответ представления за вас:

from django.views.decorators.cache import cache_page


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

cache_page принимает единственный аргумент: время кэширования, в секундах. В приведённом выше примере результат представления my_view() будет кэшироваться в течение 15 минут. (Обратите внимание, что мы написали его как 60 * 15, для лучшей читабельности. 60 * 15 будет вычислено как 900 – то есть 15 минут, умноженных на 60 секунд в минуте.)

Время кэширования, установленное cache_page, имеет приоритет над директивой max-age из заголовка Cache-Control.

Кэш на уровне представления, как и кэш на уровне сайта, использует URL в качестве ключа. Если несколько URL указывают на одно и то же представление, каждый URL будет кэшироваться отдельно. Продолжая пример my_view, если ваш URLconf выглядит так:

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

то запросы к /foo/1/ и /foo/23/ будут кэшироваться отдельно, как вы, вероятно, ожидаете. Но как только определённый URL (например, /foo/23/) был запрошен, последующие запросы к этому URL будут использовать кэш.

cache_page также может принимать необязательный именованный аргумент cache, который направляет декоратор использовать определённый кэш (из вашей настройки CACHES) при кэшировании результатов представления. По умолчанию используется кэш default, но вы можете указать любой другой кэш:

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

Вы также можете переопределить префикс кэша на уровне представления. cache_page принимает необязательный именованный аргумент key_prefix, который работает аналогично настройке CACHE_MIDDLEWARE_KEY_PREFIX для middleware. Его можно использовать так:

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

Аргументы key_prefix и cache могут быть указаны вместе. Аргумент key_prefix и KEY_PREFIX, указанный в CACHES, будут конкатенированы.

Кроме того, cache_page автоматически устанавливает заголовки Cache-Control и Expires в ответе, которые влияют на кэши нижнего уровня.

Указание кэша на уровне представления в URLconf

Примеры в предыдущем разделе жестко задают факт кэширования представления, поскольку cache_page изменяет функцию my_view на месте. Этот подход связывает ваше представление с системой кэширования, что не является идеальным по нескольким причинам. Например, вы можете захотеть повторно использовать функции представления на другом, некэшируемом сайте, или вы можете захотеть распределить представления людям, которые могут захотеть использовать их без кэширования. Решение этих проблем заключается в указании кэша на уровне представления в URLconf, а не рядом с самими функциями представления.

Вы можете сделать это, обернув функцию представления с помощью cache_page, когда вы ссылаетесь на неё в URLconf. Вот предыдущий URLconf:

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

Вот то же самое, но с my_view обернутым в cache_page:

from django.views.decorators.cache import cache_page

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

Кэширование фрагментов шаблона

Если вам нужно больше контроля, вы также можете кэшировать фрагменты шаблонов, используя тег cache. Чтобы предоставить вашему шаблону доступ к этому тегу, поместите {% load cache %} в начале своего шаблона.

Тег {% cache %} кэширует содержимое блока на заданный срок. Он принимает как минимум два аргумента: время кэширования в секундах и имя фрагмента кэша. Фрагмент кэшируется навсегда, если время истечения равно None. Имя используется как есть, не используйте переменные. Например:

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

Иногда вы можете захотеть кэшировать несколько копий фрагмента, в зависимости от некоторых динамических данных, которые появляются внутри фрагмента. Например, вы можете захотеть отдельные кэшированные копии боковой панели, используемой в предыдущем примере, для каждого пользователя вашего сайта. Достигается это путём передачи одного или нескольких дополнительных аргументов, которые могут быть переменными с или без фильтров, тегу {% cache %} для уникальной идентификации фрагмента кэша:

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

Если USE_I18N установлено в True, кэш middleware на уровне сайта будет учитывать активный язык. Для тега cache вы можете использовать одну из переменных, специфичных для перевода, доступных в шаблонах, для достижения того же результата:

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

{% get_current_language as LANGUAGE_CODE %}

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

Время истечения кэша может быть переменной шаблона, при условии, что переменная шаблона разрешается в целочисленное значение. Например, если переменная шаблона my_timeout имеет значение 600, то следующие два примера эквивалентны:

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

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

По умолчанию тег кэша будет пытаться использовать кэш с именем «template_fragments». Если такого кэша нет, он перейдёт к использованию кэша по умолчанию. Вы можете выбрать другой кэш-бэкэнд для использования с ключевым аргументом using, который должен быть последним аргументом тега.

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

Указание имени кэша, который не сконфигурирован, считается ошибкой.

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

Если вы хотите получить ключ кэша, используемый для кэшированного фрагмента, вы можете использовать make_template_fragment_key. fragment_name – это то же самое, что и второй аргумент тега cache; vary_on – это список всех дополнительных аргументов, переданных тегу. Эта функция может быть полезна для аннулирования или перезаписи кэшированного элемента, например:

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

API кэша низкого уровня

Иногда кэширование всей отрисованной страницы не приносит особой пользы и, фактически, является избыточным.

Например, возможно, ваш сайт содержит представление, результаты которого зависят от нескольких дорогостоящих запросов, результаты которых изменяются с разной периодичностью. В этом случае было бы нецелесообразно использовать кэширование всей страницы, которое предлагают стратегии кэширования на уровне сайта или на уровне представления, потому что вы не захотите кэшировать весь результат (так как некоторые данные часто изменяются), но вы всё равно захотите кэшировать результаты, которые редко изменяются.

Для таких случаев Django предоставляет API кэша низкого уровня. Вы можете использовать этот API для хранения объектов в кэше с любой степенью детализации, которая вам нужна. Вы можете кэшировать любой объект Python, который может быть безопасно сериализован с помощью pickle: строки, словари, списки объектов модели и так далее. (Большинство обычных объектов Python могут быть сериализованы с помощью pickle; для получения дополнительной информации о pickle обратитесь к документации 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(). Подробнее см. Поддержка асинхронности.

Префикс ключей кэша

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

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

from django.views.decorators.cache import cache_control


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

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

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

  • no_transform=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. Это связано с тем, что кэширующий средний уровень должен знать, какие заголовки использовать для изменения хранения кэша. Средний уровень всегда добавляет что-то в заголовок ответа Vary , когда это возможно.

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

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

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

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

Spec-Zone.ru

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