Redis в web-приложении на Python и Starlette
programming
Redis — это noSQL база данных. В рамках web-приложения на Python и Starlette, процесс разработки которого мы детально изучаем в этом цикле статей, будет необходимо временно хранить некоторые данные с заданным периодом автоматического удаления этих данных, и именно эту задачу я намерен решать с помощью Redis и его инструментов. В этом выпуске цикла я покажу процесс подключения к Redis в коде ASGI-приложения и получения из базы порции данных при обработке стартовой страницы.
Установка Redis
Redis — это клиент-серверное приложение. Сервер Redis получает данные от клиента и упорядоченно хранит их. Клиент даёт возможность получить доступ к серверу по сети. В Debian и deb-основанных операционных системах установить всё это великолепие можно с помощью пакетного менеджера apt, если он подключен к сетевому хранилищу пакетов.
$ sudo apt install redis
В рамках этой и последующих демонстраций я буду использовать Redis для разработки, отладки и тестирования своего web приложения, и доступ к базе данных будет осуществляться только с localhost. В этом случае начальные настройки программы в состоянии из коробки меня полностью устроят.
В своём начальном состоянии Redis предлагает абсолютно пустую базу данных. Подключиться к ней можно с помощью команды redis-cli — это и есть штатный клиент Redis.
$ redis-cli
Управлять базой данных Redis в клиенте достаточно не сложно, для этого предлагается набор команд для всех предусмотренных программой действий с данными. С помощью команды keys можно посмотреть текущий состав базы данных, на снимке экрана далее видно, что в базе хранится пустой массив.
Redis хранит данные в виде хешей ключ:значение
. На снимке экрана выше я показал, что в ключе с именем message
в базе на данный момент хранится самая обычная строка приветствия — Hello there!
. Задача web-разработчика на текущем этапе разработки — получить доступ к этим данным в рамках разрабатываемого на Python приложения.
Подключаем Redis в приложение
Управлять данными Redis в программе на Python мне поможет специализированная библиотека — redis. Её необходимо установить в виртуальное окружение.
$ pip install redis
На снимке экрана выше следует обратить внимание, что предложенную выше команду я исполнил в терминале, находясь в корневом каталоге приложения и с его активным виртуальным окружением.
Подключаться к базе данных я буду по сети, при этом адрес подключения укажу в файле настроек приложения. Открываю его в текстовом редакторе Vim.
$ vim .env
В самый конец этого файла дописываю следующую строчку.
REDI='redis://localhost:6379/0'
Здесь я указал протокол (redis
), адрес сервера Redis в сети (localhost
), порт (6379
) и имя базы данных (0
), к которой подключаюсь. Сохраняю изменения в файл.
Конфигурация ASGI-приложения хранится в "ините" базового каталога приложения. Открываю этот файл в командном режиме Vim.
:edit webapp/__init__.py
Только что установленную библиотеку redis, а именно, её реализацию с асинхронным вводом-выводом следует подключить соответствующей процедурой импорта.
import redis.asyncio as redis
Классу StApp
, в его метод __call__
дописываю ещё одно свойство.
self.rp = redis.ConnectionPool.from_url(
settings.get('REDI'),
health_check_interval=30,
socket_connect_timeout=15,
socket_keepalive=True,
retry_on_timeout=True,
decode_responses=True)
Здесь в переменной self.rp
с помощью библиотечных инструментов redis я создал пул подключений к базе данных, используя соответствующее поле файла настроек в качестве url и указанные параметры. Обращаю внимание на параметр decode_responses
, в созданных с помощью этого пула подключениях все ответы базы данных будут декодированы.
Как видно из представленного кода, доступ к пулу подключений Redis в рамках web приложения можно будет получить через запрос — request.app.rp
. Сохраняю изменения в файл.
Имея пул подключений Redis, можно создать подключение в любом обработчике любого url-адреса приложения. Удобно завернуть этот функционал в ещё одну вспомогательную функцию. Открываю в текстовом редакторе ещё один файл.
:edit webapp/common/redi.py
И в этом файле пишу следующий код.
import redis.asyncio as redis
async def get_rc(request):
return redis.Redis.from_pool(request.app.rp)
Код достаточно элементарный, дополнительных объяснений, я думаю, не требуется. Сохраняю изменения в файл.
Получаем данные из базы Redis
В web-приложении на текущий момент есть url-адрес стартовой страницы, и для этого адреса в модуле views.py
каталога главной подпрограммы main
определён обработчик — функция представления show_index
. В рамках этого обработчика я и собираюсь продемонстрировать доступ к данным в базе Redis. Открываю указанный модуль в текстовом редакторе.
:tabnew webapp/main/views.py
Нахожу в коде этого модуля функцию представления show_index
и дописываю в тело этой функции следующий код.
from ..common.redi import get_rc
##Создаю соединение с базой данных
rc = await get_rc(request)
##Получаю хранящуюся в ключе message базы данных
##строку и сохраняю её в одноимённой переменной
message = await rc.get('message')
##закрываю соединение с базой данных
await rc.close()
Чтобы иметь доступ к полученному в результате обработки этого кода значению переменной message
, нужно передать это значение в словарь шаблона одноимённым ключом. Внимание на следующий снимок экрана.
Сохраняю изменения в файл.
В результате всех проделанных действий, шаблону передана полученная из базы данных Redis строка, этими данными достаточно просто воспользоваться в логике шаблона на Jinja2. Открываю файл шаблона в текстовом редакторе.
:tabnew webapp/templates/main/index.html
Нахожу в коде этого шаблона элемент с классом empty-message
и модифицирую его содержимое следующим кодом.
<div class="empty-message">
{% if message %}
{{ message }}
{% else %}
Сайт в стадии разработки, попробуйте зайти позже.
{% endif %}
</div>
Здесь всё просто... Если в словаре шаблона имеется ключ с именем message
, то в блоке помещаем хранящуюся в этом ключе строку. В ином случае в блоке будет размещена строка из шаблона.
Сохраняю изменения в файл.
Тестируем в браузере
Подтвердить только что разработанный функционал можно самым простым тестом.
Первое, чего я ожидаю, это штатный запуск отладочного сервера без отчётов об ошибках и предупреждений в его выводе в терминал. Запускаю, внимание на следующий снимок экрана.
Второе, чего я ожидаю, в блоке с информационным сообщением о стадии разработки сайта должна появиться хранящаяся в базе данных Redis, в соответствующем её ключе строка. Запускаю браузер и стучусь по адресу стартовой страницы приложения... Внимание на следующий снимок экрана.
Как видно на снимке, моя гипотеза полностью подтвердилась, и текст сообщения стартовой страницы соответствует ключу message
базы данных Redis. Конечно же, следует посмотреть в окно терминала и убедиться, что отладочный сервер отдаёт в терминал свой обычный вывод, и в нём нет сообщений об ошибках и предупреждений.
Можно инициировать ещё один простой тест, удалить из базы данных Redis ключ message
и обновить страницу в браузере. Как вы думаете, что покажет браузер в этом случае? Напишите в комментариях.
Подводим промежуточный итог
В web-приложении на Python и Starlette в результате всех предпринятых на текущем этапе разработки действий появилась база данных Redis и возможность хранить в ней данные, получать их, когда это необходимо, и модифицировать в соответствии с замыслом разработки и техническим заданием на проект. Текущую версию кода, как всегда, можно увидеть в моём профиле на github.com по этой короткой ссылке. Все цели этой демонстрации полностью достигнуты. О продолжении разработки этого web-приложения я расскажу в следующих выпусках этого блога. Stay tunned!