null

Кэширование в Django

Введение

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

Django Cache поддерживает различные типы кэш-провайдеров, такие как Memcached или Redis. Кэширование может быть применено к представлениям (views), фрагментам шаблонов (template fragments) и результатам запросов к базе данных и их обработки.

Установка и настройка

Настроить кэш в Django несложно. Для этого нужно просто добавить в файл с настроками settings.py параметры провайдера. Пример для Memcached:

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

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

Данный вид кэширования может быть полезен, если ваши представления (views) не зависят от контекста исполнения. Например, представление, которое отдает некоторую выборку из базы данных. Пример: 

from django.views.decorators.cache import cache_page

@cache_page(60 * 15)  # Кэшируем представление на 15 минут
def my_view(request):
    # Реализация представления
    # ...
    return HttpResponse(result)

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

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

from django.views.decorators.cache import cache_page
from django.template.loader import render_to_string

def expensive_fragment():
    # Реализация дорогостоящего фрагмента
    # ...

    # Возвращаем результат фрагмента
    return result

def my_view(request):
    fragment = cache.get('my_fragment_cache_key')

    if fragment is None:
        # Фрагмент не найден в кэше, выполняем дорогостоящую операцию
        fragment = expensive_fragment()

        # Сохраняем фрагмент в кэше на 5 минут
        cache.set('my_fragment_cache_key', fragment, 300)

    context = {
        'fragment': fragment,
    }

    return HttpResponse(render_to_string('my_template.html', context))

Использование для кэширования данных из базы данных

Наконец, вероятно наиболее популярный случай использования серверного кэша: сохранение некоторого набора данных, полученных из БД. Популярность обусловлена тем, что в среднем получение данных из кэша намного быстрее выборки из базы. К тому же, данные можно закэшировать уже после их обработки кодом. Это также положительно скажется на производительности, т.к. практика показывает, что обработка большого объема данных в Python бывает очень медленной.

Пример такого подхода:

from django.core.cache import cache
from .models import Product

def get_products():
    products = cache.get('products_list')

    if products is None:
        # Результат не найден в кэше, выполняем запрос к базе данных
        products = Product.objects.all()
        # Выполняем обработку
        products = ...

        # Сохраняем результат в кэше на 1 час
        cache.set('products_list', products, 3600)

    return products

Стоит также отметить то, что можно также настроить раздельное кэширование для запросов пользователей. Для этого достаточно в формирование ключа включить идентификатор пользователя:

cache.set(f'products_list_{user.username}', products, 3600)
cache.get(f'products_list_{user.username}')

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

Инвалидация кэша

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

from django.core.cache import cache
from .models import Product

def update_product(product_id):
    # Обновление данных о продукте в базе данных
    # ...

    # Инвалидация соответствующего элемента кэша
    cache.delete('product_{}'.format(product_id))

Выводы

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