Учим_Питон_#5 — Практика: асинхронные функции

Внимание! Данный материал может содержать ошибки, и является лишь попыткой закрепить изученный материал через изложение. Комментарии с правками приветствуются.
Используем:
- Python 3.9
- Asyncio
- Aiohttp
- Postgresql
- Docker
- Sqlalchemy
Задача: написать асинхронный код, который скачивает данные с сайта и загружает их в БД.
Создаем 5 файлов:
- main.py — точка входа для запуска программы
- jsonplaceholder.py — модуль, скачивающий данные с сайта
- models.py — ф-и для БД
- docker-compose.yaml — информация для докера
- postgres.env — логин, пароль, название БД
docker-compose.yaml:
postgres.env
jsonplaceholder.py
- aiohttp — это «async HTTP client/server for asyncio and Python».
- asyncio — это «a library to write concurrent code using the async/await syntax».
- async — создает корутину
- await — позволяет переключить контекст, переходя к другой ф-и, если текущая занята.
- async with — асинхронный менеджер контекста, который автоматически закроется после выполнения задачи (см. Менеджер контекста или with as)
Корутина — cooperative routine, код, который может выполняться одновременно с другим кодом.
Что мы здесь делаем:
-
Импортируем библиотеки Пишем константы (по соглашению разработчиков, константы — это переменные, записанные с большой буквы, и которые НЕ следует менять!)
-
Создаем асинхронную ф-и(корутину) fetch_json — дословно: «получить json».
-
Внутри ф-и вызываем асинхронный менеджер, в нем открываем сессию через aiohttp, и следующим шагом посылаем запрос GET по урлу session.get(url). Результат кладем в переменную response и возвращаем его, преобразовав в json (return await response.json()) — код с return читается справа налево. Вначале await переменной, затем return того, что получилось)
Важно: данные, приходящие с сервера, не нужно мутировать! Почему? Потому что другие разработчики знают, как работает json() и ожидают json, а не мутанта:) Не усложняйте жизнь другим и себе, например, спустя месяц.
Итак, результат работы модуля jsonplaceholder.py — возврат данных в виде json
Импортируем модуль даты и времени
from datetime import datetime
Импортируем логгер (типа дебаггера)
Импортируем ORM для работы с БД
Импортируем асинхронные методы sqlalchemy для работы с БД
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
Импортируем метод работы с бд, фабрику сессий и связи
Создаем движок
Создаем метод описания БД (Создаем базовый класс для декларативных определений классов.)
Создаем сессию (Фабрика sessionmaker генерирует новые объекты Session при вызове)
Session = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
Запускаем докер, вызвав команду в консоли используя create_subprocess_shell(cmd)
cmd = 'docker compose up -d'
async def create_pg_docker(cmd):
result = await asyncio.create_subprocess_shell(cmd)
await result.communicate()
logger.info('____pg docker rdy')
Делаем DROP TABLE, CREATE TABLE в БД
Cоздаем ф-ю, которая скачивает с сайта юзеров и сохраняет их в БД
Cоздаем ф-ю, которая скачивает с сайта посты и сохраняет их в БД
Создаем модель таблицы User и Post
Результат работы модуля models.py:
- Подключаемся к БД в докере
- Дропаем и создаем заново таблицы (чтобы небыло конфликта с существующими записями)
- Сохраняем данные юзеров и постов в БД
main.py:
Что здесь делаем:
Создаем асинхронный main() и запускаем его в синхронном main(). Внутри async_main() последовательно запускаем асинхронные ф-и:
- Запуск БД в докере
- Дроп и создание пустых таблиц
- Получение данных юзеров и постов с сайта
- Сохранение юзеров в бд
- Сохранение постов в бд
- Закрытие подключения к бд (происходит неявно, автоматически при завершении работы менеджера контекста)
- asyncio.gather — метод, который вернет то, что в него было положено, в том же порядке. (deprecated in python 3.10!!!)
- asyncio.run - метод, создающий event loop для асинхронных ф-ий.
Результат работы main.py: В БД, в таблице User появляется 10 пользователей, в таблице Post — 100 записей, связанных отношением many to one к юзерам, написавших их.
P.S. Если будете копировать код - используйте IDE для его запуска т.к. где-то может стоять 4 пробела, а где-то табуляция. Но лучше не копировать, а руками перепечатать, так лучше запомнится и поймется.
Valeriy Poltoranin
5 лет назад
1





