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

К концу этой инструкции главная страница вашего проекта на Django будет отдаваться из кеша, а не собираться заново на каждый запрос. Разница видна сразу: страница, которая делает пару запросов к базе и рендерит десяток шаблонов, после кеширования отвечает за единицы миллисекунд вместо десятков или сотен. На настройку уйдёт около 30–40 минут, если Redis уже установлен, и час, если ставить его с нуля.
Инструкция написана под Django 5.x (актуально и для 4.2 LTS). Разбираем два варианта бэкенда: встроенный кеш в локальной памяти для быстрой проверки и Redis для боевого сервера. Кеширование отдельных фрагментов шаблона и инвалидацию кеша при изменении данных здесь не трогаем — это отдельные темы, тут только целая страница.
Что нужно иметь до начала
- Рабочий проект на Django 5.x, который запускается локально командой
python manage.py runserver. - Главная страница, привязанная к view — функции или классу. Кешировать будем именно её.
- Python 3.10 или новее (требование Django 5.x).
- Для варианта с Redis — установленный и запущенный Redis 6 или 7. Проверить: команда
redis-cli pingдолжна ответитьPONG. - Доступ к файлу
settings.pyи возможность перезапустить сервер.
Если Redis не установлен и ставить его прямо сейчас некогда — начните с локального кеша в памяти, он не требует ничего внешнего. Для боевого сервера он не годится, но чтобы увидеть эффект и понять механику — подходит.
Шаг 1. Выбираем и настраиваем бэкенд кеша
Django хранит кеш где-то: в оперативной памяти процесса, в файлах, в базе или во внешнем хранилище вроде Redis. Настройка живёт в словаре CACHES в settings.py.
Вариант A — локальная память (для проверки)
Ничего ставить не нужно. Добавьте в settings.py:
# settings.py — кеш в памяти процесса, только для локальной проверки
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
"LOCATION": "unique-snowflake",
}
}Проверка: сервер запускается без ошибок, в консоли нет строк про CACHES. Кеш живёт внутри одного процесса и обнуляется при перезапуске — это нормально для этого варианта.
Вариант B — Redis (для боевого сервера)
В Django 5.x есть встроенный бэкенд для Redis, отдельный пакет-обёртка не нужен, но клиент redis для Python поставить придётся:
pip install redisДальше в settings.py:
# settings.py — кеш в Redis, встроенный бэкенд Django 5.x
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.redis.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
}
}Здесь /1 в конце — номер базы Redis (0–15 по умолчанию). Отделите кеш от других данных, взяв, например, базу 1.
Проверка: запустите оболочку Django и положите значение в кеш.
python manage.py shell>>> from django.core.cache import cache
>>> cache.set("проверка", "работает", 30)
>>> cache.get("проверка")
'работает'Если вернулось 'работает' — бэкенд подключён. Если None сразу после set — соединение с Redis не установлено, смотрите раздел про ошибки.
Шаг 2. Кешируем главную страницу
Самый прямой способ — декоратор cache_page. Он оборачивает view и кладёт весь HTTP-ответ в кеш на заданное число секунд.
Если главная — функция-view
# views.py
from django.views.decorators.cache import cache_page
# кешируем ответ на 300 секунд (5 минут)
@cache_page(60 * 5)
def home(request):
articles = Article.objects.filter(is_published=True)[:10]
return render(request, "home.html", {"articles": articles})Если главная — класс-view
Декоратор нельзя повесить прямо на класс, его оборачивают через method_decorator или применяют в urls.py. Второй способ короче:
# urls.py
from django.views.decorators.cache import cache_page
from .views import HomeView
urlpatterns = [
# оборачиваем view прямо в маршруте
path("", cache_page(60 * 5)(HomeView.as_view()), name="home"),
]Проверка: откройте главную в браузере, обновите пару раз. Дальше — как убедиться, что ответ идёт из кеша, в следующем шаге.
Шаг 3. Убеждаемся, что кеш действительно работает
Открыть страницу и увидеть, что она грузится, — не проверка. Нужно доказать, что второй запрос не собирает страницу заново.
- Временно добавьте в начало view строку
print("собираю страницу заново"). - Обновите главную три раза подряд.
- Смотрите в консоль сервера: строка должна появиться один раз, а не три. Остальные два запроса взялись из кеша.
- Уберите
printпосле проверки.
Второй способ — заголовки ответа. При включённом cache_page Django добавляет заголовки Cache-Control и Expires. Откройте вкладку Network в браузере и посмотрите на них — там будет max-age=300.
Кеш ускоряет не «сайт вообще», а конкретный ответ на конкретный URL. Пока вы не убедились, что второй запрос не дошёл до базы, вы не ускорили ничего — вы поверили, что ускорили.
Шаг 4. Осторожно с персонализацией
Тут прячется самая частая беда новичка. cache_page кеширует ответ для всех подряд. Если главная показывает «Здравствуйте, Иван» для залогиненного и «Войти» для гостя, то первый попавший на страницу гость закеширует гостевой вариант — и Иван увидит «Войти». Или наоборот.
Правила простые:
- Страница одинакова для всех — кешируйте
cache_pageсмело. - Страница зависит от пользователя — либо не кешируйте её целиком, либо выносите персональный блок в отдельный запрос (например, подгружайте приветствие через отдельный маршрут без кеша), либо переходите на кеширование фрагментов шаблона.
- Страница зависит от языка, валюты или другого заголовка — используйте декоратор
vary_on_headersвместе сcache_page, чтобы Django хранил отдельную версию под каждый вариант заголовка.
Пример разделения кеша по языку:
# views.py
from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_headers
@cache_page(60 * 5)
@vary_on_headers("Accept-Language") # своя копия кеша под каждый язык
def home(request):
...Если не получилось
| Что видите | Причина | Что делать |
|---|---|---|
ConnectionError: Error connecting to Redis | Redis не запущен или адрес в LOCATION неверный | Проверьте redis-cli ping, сверьте хост и порт в строке redis://... |
ModuleNotFoundError: No module named 'redis' | Не установлен Python-клиент | Выполните pip install redis в том же окружении, где запущен проект |
| Залогиненный пользователь видит чужую страницу | Кешируется персонализированный ответ | Уберите cache_page с этой страницы или вынесите персональный блок, см. Шаг 4 |
| Изменил данные — страница не обновляется 5 минут | Это работает как задумано: кеш живёт заданное время | Уменьшите время в cache_page или настройте инвалидацию (отдельная тема) |
cache.get всегда возвращает None | Кеш не сохраняется: неверный бэкенд или для LocMem — новый процесс | Проверьте CACHES, для LocMem помните, что кеш обнуляется при перезапуске |
Что делать дальше
Целая страница закеширована — это грубый, но самый эффективный уровень. Следующие шаги, когда упрётесь в него:
- Настройте инвалидацию: сбрасывайте ключ страницы при сохранении статьи через сигнал
post_save, чтобы не ждать истечения времени. - Если персонализации много — переходите на низкоуровневый API
cache.get/cache.setи кешируйте отдельные тяжёлые запросы к базе, а не весь ответ. - Мерьте эффект: смотрите время ответа в логах Nginx или через инструмент вроде django-debug-toolbar на локали. Показатель, за которым следить в проде, — доля попаданий в кеш (hit rate) в Redis: команда
redis-cli info statsпокажетkeyspace_hitsиkeyspace_misses.
Задачи: математика, физика, химия: Разбор задачи по шагам с проверкой и похожей задачей
Спросить за 10 ₽Источники: Django documentation — Django's cache framework, Django documentation — django.views.decorators.cache
Частые вопросы
У меня Django 4.2, а не 5.x — инструкция подойдёт?
Что выбрать: локальную память или Redis?
Кеш стоит, а страница всё равно не обновляется после изменения данных — это баг?
Можно ли кешировать страницу, где показывается имя залогиненного пользователя?
Не сработало — cache.get возвращает None сразу после cache.set. В чём дело?
Кеш замедлил разработку — вижу старые данные при отладке. Как отключить на локали?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.