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

См.также

В :ref:`Философия проектирования Cache Framework <cache-design-philosophy> объясняются некоторые проектные решения этой платформы.

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

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

Ваши предпочтения кеширования находятся в параметре 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 (в зависимости от выбранного вами привязки)
  • Установите LOCATION на значения ip:port, где ip - это IP-адрес демона Memcached, а port - это порт, на котором работает Memcached, или в значение unix:path, где path - это путь к файлу сокета Memcached Unix.

В этом примере Memcached работает на порту 11211 localhost (127.0.0.1) с использованием привязки 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 - это база данных in-memory, которую можно использовать для кэширования. Для начала работы вам понадобится сервер Redis, запущенный либо локально, либо на удаленной машине.

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

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

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

Например, если Redis работает на localhost (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

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

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

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

Серверная часть на основе файлов сериализует и сохраняет каждое значение кэша как отдельный файл. Чтобы использовать этот бэкэнд, установите 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.

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

Когда параметр cache 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. На некоторых бэкэндах (в частности, в базе данных) это делает отсечение намного быстрее за счет большего количества промахов в кеше.

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

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

    Более подробную информацию см. в cache documentation.

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

    Более подробную информацию см. в cache documentation.

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

    Более подробную информацию см. в cache documentation.

В этом примере серверная часть файловой системы настраивается с таймаутом 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, который позволяет объединять клиентов в пул (что может повысить производительность за счет сохранения подключенных клиентов), обрабатывает ошибки кэша памяти/сети как пропуски кеша и устанавливает флаг TCP_NODELAY на сокете соединения:

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

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

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

Кеширование на уровне сайта

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

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

Примечание

Нет, это не опечатка: промежуточное ПО «update» должно быть первым в списке, а промежуточное ПО «fetch» должно быть последним. Детали немного неясны, но если вам нужна полная история, смотрите Порядок MIDDLEWARE ниже.

Затем добавьте следующие необходимые настройки в файл настроек Django:

  • CACHE_MIDDLEWARE_ALIAS - Псевдоним кеша, используемый для хранения.
  • CACHE_MIDDLEWARE_SECONDS – Количество секунд, в течение которых каждая страница должна быть кэширована.
  • CACHE_MIDDLEWARE_KEY_PREFIX – Если кеш используется на нескольких сайтах, использующих одну и ту же установку Django, задайте здесь имя сайта или другую строку, уникальную для этого экземпляра Django, чтобы предотвратить конфликты ключей. Если вам все равно, используйте пустую строку.

FetchFromCacheMiddleware кэширует ответы GET и HEAD со статусом 200, если это позволяют заголовки запроса и ответа. Ответы на запросы одного и того же URL с разными параметрами запроса считаются уникальными страницами и кэшируются отдельно. Это промежуточное ПО ожидает, что на запрос HEAD ответят те же заголовки, что и на соответствующий запрос GET; в этом случае он может вернуть кешированный ответ GET на запрос HEAD.

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

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

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

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

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

Ключи кэша также включают current time zone, когда 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 для промежуточного программного обеспечения. Его можно использовать так:

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

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

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

Указание кеша для каждого представления в 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, кеш промежуточного программного обеспечения для каждого сайта будет учитывать активный язык <i18n-cache-key> `. Для тега шаблона ``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; var_on - это список всех дополнительных аргументов, переданных тегу. Эта функция может быть полезна для аннулирования или перезаписи кэшированного элемента, например:

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

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

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

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

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

Доступ к кешу

django.core.cache.caches

Вы можете получить доступ к кешам, настроенном в параметре CACHES, через 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 приведет к кешированию значения навсегда. Значение тайм-аута, равное 0, не кэшируется.

Если объект не существует в кэше, то cache.get() возвращает None:

>>> # Wait 30 seconds for 'my_key' to expire...
>>> cache.get("my_key")
None

Если необходимо определить, существует ли объект в кэше, а в памяти хранится литеральное значение None, используйте объект sentinel по умолчанию:

>>> 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

touch() возвращает 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()

Примечание

Для кешей, которые не реализуют методы закрытия, это не работает.

Примечание

Асинхронные варианты базовых методов имеют префикс a, например, cache.aadd() или cache.adelete_many(). Более подробную информацию смотрите в Asynchronous support.

Префикс ключа кеширования

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

Чтобы предотвратить это, 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 имеет асинхронные варианты all base methods. По условию, асинхронные версии всех методов имеют префикс a. По умолчанию аргументы для обоих вариантов одинаковы:

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

Последующие кеши

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

Вот несколько примеров нижестоящих кешей:

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

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

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

К счастью, 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) будет кэшировать отдельную версию страницы для каждого уникального пользовательского агента.

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

Вы можете передать несколько заголовков в var_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):
    ...

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

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

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 см. в разделе official Vary spec.

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

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

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

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

from django.views.decorators.cache import cache_control


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

Этот декоратор заботится об отправке соответствующего HTTP-заголовка за кулисами.

Обратите внимание, что параметры управления кешем «частный» и «общедоступный» являются взаимоисключающими. Декоратор гарантирует, что директива «public» будет удалена, если необходимо установить «private» (и наоборот). Примером использования двух директив может быть сайт блога, который предлагает как частные, так и публичные записи. Общедоступные записи могут храниться в любом общем кэше. В следующем коде используется 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

Управлять нисходящим кэшем можно и другими способами (подробнее о HTTP-кэшировании см. в RFC 9111). Например, даже если вы не используете серверный кэш 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 adds Cookie
  • GZipMiddleware adds Accept-Encoding
  • LocaleMiddleware adds Accept-Language

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

Вернуться на верх