Spec-Zone.ru › Django 4.2

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

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

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

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

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

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

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

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

См. также

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

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

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

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

Memcached

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

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

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

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

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

В этом примере Memcached работает на локальном хосте (127.0.0.1) на порту 11211, используя привязку pymemcache.

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
        "LOCATION": "127.0.0.1:11211",
    }
}

В этом примере Memcached доступен через локальный файл сокета /tmp/memcached.sock с использованием привязки pymemcache:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
        "LOCATION": "unix:/tmp/memcached.sock",
    }
}

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

В этом примере кэш разделяется между экземплярами Memcached, работающими по IP-адресам 172.19.26.240 и 172.19.26.242, оба на порту 11211:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
        "LOCATION": [
            "172.19.26.240:11211",
            "172.19.26.242:11211",
        ],
    }
}

В следующем примере кэш разделяется между экземплярами Memcached, работающими по IP-адресам 172.19.26.240 (порт 11211), 172.19.26.242 (порт 11212) и 172.19.26.244 (порт 11213):

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
        "LOCATION": [
            "172.19.26.240:11211",
            "172.19.26.242:11212",
            "172.19.26.244:11213",
        ],
    }
}

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

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

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

Redis

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

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

Для использования Redis в качестве бэкенда кэширования с Django:

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

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

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

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

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

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

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

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

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

Для использования таблицы базы данных в качестве бэкенда кэширования:

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

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

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

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

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

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

python manage.py createcachetable

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Это полезно, если у вас есть сайт в рабочей среде, который использует мощное кэширование в различных местах, но среда разработки/тестирования, где вы не хотите кэшировать и не хотите изменять свой код для специальных случаев последней среды. Для активации кэширования «пустышки» установите BACKEND следующим образом:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.dummy.DummyCache",
    }
}

Использование пользовательского кэша

Хотя Django включает поддержку ряда кэшей «из коробки», иногда вам может потребоваться использовать настроенный кэш. Для использования внешнего кэша с Django используйте путь импорта Python в качестве BACKEND настройки CACHES, как показано ниже:

CACHES = {
    "default": {
        "BACKEND": "path.to.backend",
    }
}

Если вы разрабатываете свой собственный кэш, вы можете использовать стандартные кэши как эталонные реализации. Вы найдете код в каталоге django/core/cache/backends/ исходного кода Django.

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

Аргументы кэша

Каждый кэш может иметь дополнительные аргументы для управления поведением кэширования. Эти аргументы предоставляются как дополнительные ключи в настройке CACHES. Допустимые аргументы следующие:

  • TIMEOUT: Значение времени ожидания по умолчанию в секундах для кэша. Этот аргумент по умолчанию равен 300 секундам (5 минут). Вы можете установить TIMEOUT в значение None, чтобы ключи кэша по умолчанию никогда не истекали. Значение 0 приводит к немедленному истечению срока действия ключей (эффективно «не кэшировать»).
  • OPTIONS: Любые параметры, которые должны быть переданы в кэш. Список допустимых параметров будет варьироваться в зависимости от каждого кэша, и кэши, основанные на сторонней библиотеке, будут передавать свои параметры непосредственно базовой библиотеке кэширования.

    Кэши, реализующие свою собственную стратегию удаления устаревших данных (то есть кэши locmem, filesystem и database), будут учитывать следующие параметры:

    • MAX_ENTRIES: Максимальное количество элементов, разрешенное в кэше, прежде чем устаревшие значения будут удалены. Этот аргумент по умолчанию равен 300.
    • CULL_FREQUENCY: Доля элементов, которые будут удалены при достижении MAX_ENTRIES. Фактическая доля равна 1 / CULL_FREQUENCY, поэтому установите CULL_FREQUENCY в 2, чтобы удалить половину элементов при достижении MAX_ENTRIES. Этот аргумент должен быть целым числом и по умолчанию равен 3.

      Значение 0 для CULL_FREQUENCY означает, что весь кэш будет очищен при достижении MAX_ENTRIES. В некоторых кэшах (например, в database) это значительно ускоряет процесс удаления устаревших данных в ущерб увеличению промахов кэша.

    Кэши Memcached и Redis передают содержимое OPTIONS в качестве ключевых аргументов конструкторам клиента, что позволяет более точно управлять поведением клиента. Пример использования приведен ниже.

  • KEY_PREFIX: Строка, которая будет автоматически включена (по умолчанию в качестве префикса) ко всем ключам кэша, используемым сервером Django.

    Дополнительную информацию см. в документации по кэшу.

  • VERSION: Номер версии по умолчанию для ключей кэша, генерируемых сервером Django.

    Дополнительную информацию см. в документации по кэшу.

  • KEY_FUNCTION Строка, содержащая путь к функции, определяющей, как комбинировать префикс, версию и ключ в конечный ключ кэша.

    Дополнительную информацию см. в документации по кэшу.

В этом примере файловая база данных настраивается с временем ожидания 60 секунд и максимальной емкостью 1000 элементов:

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

Вот пример конфигурации кэша на основе pylibmc, который включает двоичный протокол, аутентификацию SASL и режим работы ketama:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.memcached.PyLibMCCache",
        "LOCATION": "127.0.0.1:11211",
        "OPTIONS": {
            "binary": True,
            "username": "user",
            "password": "pass",
            "behaviors": {
                "ketama": True,
            },
        },
    }
}

Вот пример конфигурации кэша на основе pymemcache, который включает пул клиентов (что может повысить производительность за счет сохранения подключений клиентов), обрабатывает ошибки memcache/сети как промахи кэша и устанавливает флаг TCP_NODELAY на сокете подключения:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
        "LOCATION": "127.0.0.1:11211",
        "OPTIONS": {
            "no_delay": True,
            "ignore_exc": True,
            "max_pool_size": 4,
            "use_pooling": True,
        },
    }
}

Вот пример конфигурации кэша на основе redis, который выбирает базу данных 10 (по умолчанию Redis поставляется с 16 логическими базами данных), указывает класс парсера (redis.connection.HiredisParser используется по умолчанию, если пакет hiredis-py установлен) и задает пользовательский класс пула соединений (redis.ConnectionPool используется по умолчанию):

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

Кэш для всего сайта

После настройки кэша самый простой способ его использования — кэширование всего сайта. Вам нужно добавить 'django.middleware.cache.UpdateCacheMiddleware' и 'django.middleware.cache.FetchFromCacheMiddleware' в настройку MIDDLEWARE, как в этом примере:

MIDDLEWARE = [
    "django.middleware.cache.UpdateCacheMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.cache.FetchFromCacheMiddleware",
]

Примечание

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

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

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

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

Если USE_I18N установлено в True, кэш 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 кэширования низкого уровня

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

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

END_OF_DOCUMENT_MARKER ```

В таких случаях Django предоставляет низкоуровневый API кэша. Вы можете использовать этот API для хранения объектов в кэше с любой степенью детализации. Вы можете кэшировать любые объекты Python, которые можно безопасно закешировать: строки, словари, списки объектов модели и так далее. (Большинство обычных объектов Python можно закешировать; обратитесь к документации Python для получения дополнительной информации о кэшировании.)

Доступ к кэшу

django.core.cache.caches

Вы можете получить доступ к настроенным кэшам в настройке CACHES через объект типа dict: 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.

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

Версии кэша

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

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

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

>>> # Set version 2 of a cache key
>>> cache.set("my_key", "hello world!", version=2)
>>> # Get the default version (assuming version=1)
>>> cache.get("my_key")
None
>>> # Get version 2 of the same key
>>> cache.get("my_key", version=2)
'hello world!'

Версию конкретного ключа можно инкрементировать и декрементировать с помощью методов incr_version() и decr_version(). Это позволяет повысить версию конкретных ключей, оставив другие ключи без изменений. Продолжая наш предыдущий пример:

>>> # Increment the version of 'my_key'
>>> cache.incr_version("my_key")
>>> # The default version still isn't available
>>> cache.get("my_key")
None
# Version 2 isn't available, either
>>> cache.get("my_key", version=2)
None
>>> # But version 3 *is* available
>>> cache.get("my_key", version=3)
'hello world!'

Преобразование ключей кэша

Как описано в предыдущих двух разделах, ключ кэша, предоставленный пользователем, не используется напрямую – он объединяется с префиксом кэша и версией ключа, чтобы получить окончательный ключ кэша. По умолчанию три части объединяются с помощью двоеточий, чтобы получить конечную строку:

def make_key(key, key_prefix, version):
    return "%s:%s:%s" % (key_prefix, version, key)

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

Настройка кэша KEY_FUNCTION указывает путь к функции, соответствующей прототипу make_key() выше. При наличии этой функции она будет использоваться вместо функции объединения ключей по умолчанию.

Предупреждения о ключах кэша

Memcached, наиболее часто используемый бэкенд кэша в производстве, не позволяет ключам кэша превышать 250 символов или содержать пробелы или управляющие символы, и использование таких ключей приведет к возникновению исключения. Для поощрения переносимого кода кэша и минимизации неприятных сюрпризов другие встроенные бэкенды кэша выдают предупреждение (django.core.cache.backends.base.CacheKeyWarning) при использовании ключа, который вызовет ошибку в memcached.

Если вы используете бэкенд для производства, который может принять более широкий диапазон ключей (настраиваемый бэкенд или один из встроенных бэкэндов, не основанных на memcached), и хотите использовать этот более широкий диапазон без предупреждений, вы можете отключить CacheKeyWarning с помощью этого кода в модуле management одного из ваших INSTALLED_APPS:

import warnings

from django.core.cache import CacheKeyWarning

warnings.simplefilter("ignore", CacheKeyWarning)

Если вы хотите вместо этого предоставить пользовательскую логику проверки ключей для одного из встроенных бэкэндов, вы можете создать его подкласс, переопределить только метод validate_key и следовать инструкциям по использованию пользовательского кэша бэкэнда. Например, чтобы сделать это для бэкэнда locmem, поместите этот код в модуль:

from django.core.cache.backends.locmem import LocMemCache


class CustomLocMemCache(LocMemCache):
    def validate_key(self, key):
        """Custom validation, raising exceptions or warnings as needed."""
        ...

…и используйте полное имя Python этого класса в части BACKEND вашего параметра CACHES.

Асинхронная поддержка

Django разрабатывает поддержку асинхронных кэшей, но пока не поддерживает асинхронное кэширование. Это будет реализовано в будущей версии.

django.core.cache.backends.base.BaseCache имеет асинхронные варианты всех базовых методов. По соглашению, асинхронные версии всех методов имеют префикс a. По умолчанию аргументы для обоих вариантов одинаковы:

>>> await cache.aset("num", 1)
>>> await cache.ahas_key("num")
True

Кэши нижнего уровня

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

Вот несколько примеров кэшей нижнего уровня:

  • При использовании HTTP ваш ISP может кэшировать определенные страницы, поэтому если вы запросили страницу с http://example.com/, ваш ISP пришлёт вам эту страницу, не обращаясь напрямую к example.com. Удерживающие example.com не имеют никакого знания об этом кэшировании; ISP находится между example.com и вашим браузером, обрабатывая всё кэширование прозрачно. Такое кэширование невозможно в HTTPS, так как это было бы атакой «человек посередине».
  • Ваш сайт Django может находиться за прокси-кэшем, например, Squid Web Proxy Cache (http://www.squid-cache.org/), который кэширует страницы для повышения производительности. В этом случае каждый запрос сначала обрабатывается прокси, и он передаётся вашему приложению только при необходимости.
  • Ваш веб-браузер также кэширует страницы. Если веб-страница отправляет соответствующие заголовки, ваш браузер будет использовать локальную кэшированную копию для последующих запросов на эту страницу, даже не связываясь с веб-страницей снова, чтобы увидеть, изменилась ли она.

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

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

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

Использование заголовков Vary

Заголовок Vary определяет, какие заголовки запроса механизм кэширования должен учитывать при построении ключа кэша. Например, если содержимое веб-страницы зависит от предпочтения языка пользователя, говорят, что страница «меняется в зависимости от языка».

По умолчанию система кэширования Django создаёт свои ключи кэша, используя запрашиваемый полное URL-адрес, например, "https://www.example.com/stories/2005/?order_by=author". Это означает, что каждый запрос к этому URL-адресу будет использовать ту же кэшированную версию, независимо от различий в пользовательском агенте, таких как куки или настройки языка. Однако если эта страница создаёт разное содержимое на основе некоторых различий в заголовках запроса, таких как куки, язык или пользовательский агент, вам нужно использовать заголовок Vary чтобы сообщить механизмам кэширования, что вывод страницы зависит от этих вещей.

Для этого в Django используйте удобный декоратор представления django.views.decorators.vary.vary_on_headers(), например:

from django.views.decorators.vary import vary_on_headers


@vary_on_headers("User-Agent")
def my_view(request):
    ...

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

Преимущества использования декоратора vary_on_headers вместо ручного задания заголовка Vary (используя что-то вроде response.headers['Vary'] = 'user-agent') заключается в том, что декоратор добавляет к заголовку Vary (который может уже существовать), а не устанавливает его с нуля и не перезаписывает потенциально уже имеющееся там значение.

Вы можете передать несколько заголовков в vary_on_headers():

@vary_on_headers("User-Agent", "Cookie")
def my_view(request):
    ...

Это указывает кэшам нижнего уровня на изменение в зависимости от обоих параметров, что означает, что каждая комбинация пользовательского агента и куки получит своё собственное значение кэша. Например, запрос с пользовательским агентом Mozilla и значением куки foo=bar будет считаться другим, чем запрос с пользовательским агентом Mozilla и значением куки foo=ham.

Поскольку изменение в зависимости от куки очень распространено, существует декоратор django.views.decorators.vary.vary_on_cookie(). Эти два представления эквивалентны:

@vary_on_cookie
def my_view(request):
    ...


@vary_on_headers("Cookie")
def my_view(request):
    ...

Заголовки, которые вы передаёте в vary_on_headers, не чувствительны к регистру; "User-Agent" эквивалентно "user-agent".

Также можно использовать вспомогательную функцию django.utils.cache.patch_vary_headers() напрямую. Эта функция устанавливает или добавляет к заголовку Vary header. Например:

from django.shortcuts import render
from django.utils.cache import patch_vary_headers


def my_view(request):
    ...
    response = render(request, "template_name", context)
    patch_vary_headers(response, ["Cookie"])
    return response

patch_vary_headers принимает экземпляр HttpResponse в качестве первого аргумента и список/кортеж имён заголовков (нечувствительных к регистру) в качестве второго аргумента.

Подробнее о заголовках Vary см. официальную спецификацию Vary.

Управление кэшем с помощью других заголовков

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

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

Решение заключается в том, чтобы указать, что кэш страницы должен быть «частным». Для этого в Django используйте декоратор представления cache_control(). Пример:

from django.views.decorators.cache import cache_control


@cache_control(private=True)
def my_view(request):
    ...

Этот декоратор позаботится о том, чтобы соответствующий заголовок HTTP был отправлен за кулисами.

Обратите внимание, что параметры управления кэшем «частный» и «публичный» являются взаимоисключающими. Декоратор гарантирует, что директива «публичный» удаляется, если необходимо установить «частный» (и наоборот). Пример использования этих двух директив — это блог-сайт, который предлагает как частные, так и публичные записи. Публичные записи могут кэшироваться в любом общем кэше. Следующий код использует patch_cache_control(), ручной способ модификации заголовка управления кэшем (он используется внутри декоратора cache_control()):

from django.views.decorators.cache import patch_cache_control
from django.views.decorators.vary import vary_on_cookie


@vary_on_cookie
def list_blog_entries_view(request):
    if request.user.is_anonymous:
        response = render_only_public_entries()
        patch_cache_control(response, public=True)
    else:
        response = render_private_and_public_entries(request.user)
        patch_cache_control(response, private=True)

    return response

Вы можете управлять кэшами нижнего уровня и другими способами (см. RFC 9111 для получения подробной информации о кэшировании HTTP). Например, даже если вы не используете собственный фреймворк кэширования Django на стороне сервера, вы всё равно можете сказать клиентам, чтобы они кэшировали представление в течение определённого времени с помощью директивы max-age:

from django.views.decorators.cache import cache_control


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

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

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

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

Полный список известных директив можно найти в реестре IANA (https://www.iana.org/assignments/http-cache-directives/http-cache-directives.xhtml) (обратите внимание, что не все из них применимы к ответам).

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

from django.views.decorators.cache import never_cache


@never_cache
def myview(request):
    ...

Порядок MIDDLEWARE

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

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

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

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

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

Spec-Zone.ru

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