Spec-Zone.ru › Django 5.2

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

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

Для большинства веб-приложений эти накладные расходы не являются большой проблемой. Большинство веб-приложений не washingtonpost.com или slashdot.org; это небольшие и средние сайты со средним трафиком. Но для сайтов со средним и высоким трафиком крайне важно сократить эти накладные расходы.

Именно тут и появляется кэширование.

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

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

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

Django также хорошо работает с «нижележащими» кэшами, такими как Squid и кэшами браузера. Это типы кэшей, которыми вы не управляете напрямую, но можете предоставлять подсказки (через HTTP-заголовки) о том, какие части вашего сайта должны быть кэшированы и как.

См. также

В философии проектирования системы кэширования объясняются некоторые решения, принятые при проектировании этой системы.

Настройка кэша

Система кэширования требует небольшой настройки. А именно, вы должны указать, где будут храниться кэшированные данные — в базе данных, на файловой системе или непосредственно в памяти. Это важное решение, которое влияет на производительность кэша; да, некоторые типы кэшей быстрее других.

Ваши предпочтения относительно кэша задаются в настройке CACHES в файле настроек. Вот объяснение всех доступных значений для CACHES.

Memcached

Memcached — это кэш-сервер, полностью работающий в памяти, первоначально разработанный для обработки больших нагрузок на LiveJournal.com, а затем выпущенный компанией Danga Interactive с открытым исходным кодом. Он используется такими сайтами, как Facebook и Wikipedia, для уменьшения доступа к базе данных и существенного повышения производительности сайта.

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

После установки самого Memcached вам потребуется установить привязку к Memcached. Есть несколько доступных Python-привязок к Memcached; две поддерживаемые Django — pylibmc и pymemcache.

Для использования Memcached с Django:

  • Установите BACKEND на django.core.cache.backends.memcached.PyMemcacheCache или django.core.cache.backends.memcached.PyLibMCCache (в зависимости от выбранной вами привязки к memcached)
  • Установите LOCATION на ip:port значения, где ip — IP-адрес демона Memcached, а port — порт, на котором работает Memcached, или на unix:path значение, где path — путь к файлу сокета 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).

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

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

Наконец, 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 логическими базами данных) и устанавливает пользовательский класс пула подключений (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» должен стоять первым в списке, а «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, если ваша структура URL выглядит следующим образом:

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

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

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

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

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

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

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

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

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

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

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

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

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

from django.views.decorators.cache import cache_page

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

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

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

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

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

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

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

Если USE_I18N установлен в True, кэш промежуточного слоя на уровне сайта будет учитывать активный язык. Для тега шаблона 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, который можно закодировать с помощью протокола pickle.

Аргумент 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.2/topics/cache/

Spec-Zone.ru

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