-
Notifications
You must be signed in to change notification settings - Fork 4
Redis. Middleware. Cache
Middleware - это надстройка в Django проекте, которая отрабатывает перед вью-функциями. Другими словами - это надстройка над основным приложением.
В Django уже есть встроенные Middleware, которые решают определённые задачи. Например, SessionMiddleware отвечает за сессии пользователя, когда тот подключается к приложению.
Middleware выполняются в той очередности, в которой они записаны в файле settings.py:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'shop.middleware.CacheMethodsMiddleware',
]Чтобы подключить свой модуль - достаточно указать путь к нему.
class CacheMethodsMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
return responseДля создания Middleware лучше всего использовать Class, но также можно и функцию. Class Middleware имеет 2 основных метода:
-
__init__(self, get_response)- отвечает за инициализаю Middleware. Данные метод запускается 1 раз в момент запуска приложения -
__call__(self, request)- выполняется каждый раз, когда идет обращение к View-функциям.
response = self.get_response(request) отвечает за вызов следующего в порядке Middleware, либо если это последний - View функции.
Создадим 2 Middleware и добавим функции print() до и после response = self.get_response(request).
class FirstMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
print('First call middleware')
response = self.get_response(request)
print('First end middleware')
return response
class SecondMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
print('Second call middleware')
response = self.get_response(request)
print('Second end middleware')
return responseЕсли мы запустим наше приложение и сделаем вызов View функции (которая в процессе обработки также выводим что-то в терминал), то в терминале мы увидим следующий порядок:
First call middleware
Second call middleware
View result
Second end middleware
First end middleware
Можно сказать, что это как двери. Мы переходим из одной комнаты в другую до View, и дальше а обратном порядке выходим из нее.
Кроме того, чтобы можно менять логику до и после response = self.get_response(request), также можно добавлять различные обработчики - например, обработчики ошибок. Для этого нужно создать один из методов, представленный в документации к Django Middleware.
class CacheMethodsMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
return response
def process_exception(request, exception):
# Тут можно сделать дополнительную логику обработки ошибок
return NoneСоответственно, если наше View кинуло Exception, то можно сделать обработчик и трекинг этой ошибки. Именно тут можно реализовать Alerts, Logs, и их вывод. Метод возвращает либо None, либо HttpResponse.
Для подключения Redis в качестве системы хранения информации, необходимо в settings.py добавить:
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': f'redis://{env("REDIS_HOST")}:{env("REDIS_PORT")}/',
'TIMEOUT': env('REDIS_EXP_TIME'),
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
}
}В нашем случае TIMEOUT задается через переменные окружения. Также можно его задавать через Config-file, а также использовать DEFAULT_TIMEOUT.
Данные параметр отвечает за то, сколько информация в формате ключ-значение будет храниться в Redis.
Этот параметр можно не указывать, тогда для установки времени хранения необходимо передавать данный параметр в методе set(key, value, exp_time). При этом если не передать это значение - берется DEFAULT_TIMEOUT.
Для работы с Redis используется 2 метода: get(key) и set(key, value).
Описана текущая реализация
class CacheMethodsMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
self.path = request.path_info.lstrip('/').split('/')[2]
if self.path in cache:
if cache.get(self.path) is not None:
resp = json.loads(cache.get(self.path))
return HttpResponse(resp, status=status.HTTP_200_OK)
else:
response = self.get_response(request)
cache.set(self.path, json.dumps(response.data))
return responseЧто тут происходит:
- Идет вызов от клиента к View функции
- До перехода на следующий этап мы получаем информацию о пути, по которому к нам идут
- Делаем проверку в Redis на наличие пары
key-valueпо ключу, полученному из пути - Если пара есть
- Возвращаем value для этого ключа
- Переводим это все в Json формат
- С помощью Return прекращаем дальнейшую цепочку и возвращаем пользователю ответ
- Если пары нет
- Продолжаем цепочку до View
- Получает ответ от View-функции
- Кладем в Redis данные с ключом == пути. В Value записываем данные, преобразованные в JSON-формат (потому что они приходят в формате OrderedDict)
- Возвращаем пользователю ответ от View-функции