Модуль 34

Кешування в Django

Ось готовий урок, створений за твоїм майстер-промптом.


🎓 CS50-style Lesson: Кешування в Django

Привіт, друзі! 👋 Радий бачити вас. Сьогодні ми поговоримо про те, що відрізняє "просто сайт" від сайту, який літає як ракета. Ми поговоримо про швидкість.


1. 🔥 Вступ: Чому ваш сайт "тупить"?

Уявіть, що ви прийшли до бібліотеки. Ви підходите до бібліотекаря і питаєте: "Де лежить книга 'Гаррі Поттер'?".

Бібліотекар встає, йде в архів, шукає по полицях 5 хвилин, повертається, витирає піт з чола і каже: "Третій ряд, полиця 4". Ви дякуєте і йдете.

Через хвилину підходить інший студент і питає: "Де лежить книга 'Гаррі Поттер'?". І що робить бібліотекар? Він знову йде в архів, знову шукає 5 хвилин!

Звучить абсурдно, правда? Чому б йому просто не запам'ятати це? Або не записати на стікері перед собою?

Ось так працює ваш Django-проєкт за замовчуванням. Кожного разу, коли користувач відкриває сторінку: 1. Django з’єднується з базою даних (йде в архів). 2. Виконує важкі SQL-запити (шукає книгу). 3. Рендерить HTML-шаблон (формулює відповідь). 4. Віддає сторінку.

А якщо на сайт зайде 1000 людей одночасно? Ваш "бібліотекар" (сервер) впаде від виснаження.

Сьогодні ми навчимо його користуватися "стікерами". Ми навчимо його Кешуванню.


2. 🧠 Теоретична база: Як це працює "під капотом"

Отже, кешування — це збереження результату складної операції, щоб наступного разу не робити її знову, а просто віддати готовий результат.

У комп'ютерних науках це працює як величезний словник (Dictionary), або Hash Map, якщо хочете звучати професійно. У ньому є лише дві головні речі: * Ключ (Key): Унікальне ім'я (наприклад, homepage_data). * Значення (Value): Готовий шматок даних (наприклад, готовий HTML-код або список товарів).

Як виглядає життєвий цикл запиту з кешем?

  1. Запит: Користувач стукає: "Дай мені головну сторінку!".
  2. Перевірка (Cache Hit?): Django дивиться в кеш: "Ми вже робили це?".
    • ТАК (Hit): Супер! Беремо готове, віддаємо миттєво. База даних навіть не прокидається.
    • НІ (Miss): Окей, доведеться працювати. Робимо запит в БД, рендеримо шаблон.
  3. Збереження (Set): Перед тим як віддати результат користувачу, ми кладемо його в кеш: "Запам'ятай це на 15 хвилин".

⚠️ Що треба знати залізно:

  • TTL (Time To Live): Час життя кешу. Дані не можуть жити вічно, інакше користувачі бачитимуть старі новини.
  • Backend: Де ми це зберігаємо?
    • Пам’ять (RAM): Дуже швидко (Redis, Memcached).
    • Файл/База даних: Повільніше, але надійно.
    • Local Memory: (за замовчуванням в Django) — зберігає просто в пам’яті процесу Python. Окей для розробки, погано для продакшену.

3. 🧪 Приклади: Від простого до магії

Django робить кешування неймовірно простим. Давайте подивимось.

Рівень 1: Low-level API (Ручне керування)

Уявіть, що у нас є "важка" функція, яка рахує кількість активних користувачів (це навантажує БД).

from django.core.cache import cache
from myapp.models import User
import time

def get_active_users_count():
    # 1. Спробуємо дістати з кешу по ключу 'active_users'
    count = cache.get('active_users')

    # Що ви очікуєте побачити в змінній count при першому запуску?
    # Правильно, None. Бо там ще нічого немає.

    if count is None:
        print("💡 Рахуємо чесно (довго)...")
        # Імітація важкої роботи
        time.sleep(2) 
        count = User.objects.filter(is_active=True).count()

        # 2. Зберігаємо в кеш на 60 секунд
        cache.set('active_users', count, timeout=60)
    else:
        print("🚀 Взяли з кешу (миттєво)!")

    return count

Результат: * Перший виклик: затримка 2 сек, повідомлення "Рахуємо чесно...". * Другий виклик (через 5 сек): миттєво, "Взяли з кешу!". * Третій виклик (через 61 сек): знову затримка 2 сек (бо TTL вийшов).


Рівень 2: Per-View Caching (Найпопулярніший)

Ми хочемо закешувати цілу сторінку. Навіщо писати логіку перевірки вручну, якщо можна використати декоратор?

from django.views.decorators.cache import cache_page

# Кешуємо цю сторінку на 15 хвилин (900 секунд)
@cache_page(60 * 15)
def heavy_report_view(request):
    # Тут виконуються страшні запити до БД
    data = ... 
    return render(request, 'report.html', {'data': data})

Що відбувається? Django бере URL сторінки як ключ. Якщо хтось заходить на цю ж адресу — функція heavy_report_view навіть не запускається. Django віддає збережений HTML одразу.


Рівень 3: Template Fragment Caching (Хірургічна точність)

Уявіть сайт новин. * Стрічка новин змінюється раз на годину. * Але блок "Привіт, [Ім'я Користувача]" у хедері — унікальний для кожного!

Якщо ми закешуємо всю сторінку, Василь побачить "Привіт, Петро", бо Петро зайшов першим. Це катастрофа. 😱

Тут ми кешуємо тільки шматочок шаблону:

{% load cache %}

<!-- Хедер динамічний, не кешуємо -->
<h1>Привіт, {{ user.username }}!</h1>

<!-- А ось сайдбар з погодою кешуємо на 500 секунд -->
{% cache 500 sidebar %}
    <div class="weather-widget">
        <!-- Тут якась важка логіка виводу погоди -->
        ...
    </div>
{% endcache %}

4. 🛠 Практична частина

Час забруднити руки кодом! Відкрийте ваш Django-проект (або створіть тестовий).

Завдання 1: Налаштування Додайте в settings.py просте локальне кешування (це default, але пропишіть явно, щоб бачити):

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
    }
}

Завдання 2: "Таймер" Створіть в’юху (view), яка виводить поточний час (datetime.now()). * Відкрийте сторінку -> час оновлюється при кожному F5. * Додайте декоратор @cache_page(30). * Оновіть сторінку (F5). Час завмер? Чому? Зачекайте 30 секунд. Що сталося?

Завдання 3: "Проблема свіжості" * Зробіть в’юху, яка виводить список усіх статтей з БД. Закешуйте її на 1 годину. * Зайдіть в адмінку і додайте нову статтю. * Поверніться на сторінку списку. Статті немає. Чому? * Чекпоінт: Як змусити користувачів побачити статтю, не чекаючи годину? (Підказка: гугліть cache invalidation або змініть ключ).

Завдання 4: Міні-кейс У вас є інтернет-магазин. * Ціна товару змінюється рідко. * Кількість на складі змінюється кожну хвилину. * Завдання: Використайте {% cache %} у шаблоні так, щоб опис і фото товару бралися з кешу, а ціна і наявність — завжди були актуальні.


5. 💡 Мислення як у розробника

Ви тепер знаєте, як увімкнути кеш. Але досвідчений сеньйор відрізняється від джуна не знанням команди cache.set, а розумінням того, КОЛИ її не можна використовувати.

Типові пастки (Junior Mistakes):

  1. Кешування персональних даних. Ніколи не кешуйте всю сторінку, якщо там є приватна інформація користувача. Інакше один юзер побачить корзину товарів іншого.
  2. Передчасна оптимізація. Не кешуйте все підряд. Спочатку профілюйте (заміряйте), що саме гальмує. Можливо, вам просто треба додати індекс в БД?
  3. Забута інвалідація. Найгірше для бізнесу — коли ви змінили ціну на сайті, а клієнти бачать стару ще добу.

Як думає профі:

"Є така приказка в Computer Science: 'Існує лише дві складні речі: інвалідація кешу та вигадування назв для змінних'."

Коли я додаю кеш, я завжди питаю себе: 1. Наскільки часто ці дані змінюються? (Раз на рік? Раз на секунду?) 2. Що станеться, якщо юзер побачить старі дані? (Катастрофа чи "і так зійде"?) 3. Як я буду очищати цей кеш, якщо дані зміняться? (Сигнали Django? Ручне видалення?)


6. 🧩 Підсумок

Отже, що ми сьогодні зробили? 1. Ми зрозуміли, що ходити в базу даних постійно — дорого. 2. Ми навчилися використовувати "пам'ять" (кеш), щоб зберігати готові відповіді. 3. Ми розібрали три стратегії: Low-level API, Per-View та Template Fragment.

Що ви тепер вмієте? Ви можете взяти повільну сторінку, яка вантажиться 2 секунди, і змусити її відкриватися за 20 мілісекунд. Ви фактично купуєте час за пам'ять.

🚀 Тизер наступного уроку

Але що робити, якщо операція настільки важка (наприклад, обробка відео або відправка 1000 email-ів), що користувач не може чекати навіть один раз, поки ми покладемо це в кеш? Тут нам знадобиться "магія" фонових задач. Наступного разу ми поговоримо про Celery та асинхронність.

А поки що — спробуйте закешувати щось у своєму пет-проєкті. Це магія CS50! 🏛️