asynpg-lite: Простой асинхронный менеджер для PostgreSQL на Python

Друзья, в прошлых своих публикациях я рассказывал о том, что почти в каждом своем проекте использую свой класс для работы с базой данных PostgreSQL. На днях этот класс перерос в полноценную библиотеку asynpg-lite на базе asyncpg, которую каждый из вас может прямо сейчас установить и начать использовать.

Старался сделать взаимодействие с библиотекой интуитивно понятным для каждого, а чтобы было ещё проще — всю документация внутри библиотеки написал на русском языке.

Смысл этой библиотеки в том, чтобы каждый, даже если абсолютно не знаком с SQL-запросами, получил возможность взаимодействовать с базами данных PostgreSQL, используя стандартные питоновские списки, словари и строки.

Для тестирования примеров, которые я приведу, вам потребуется база данных PostgreSQL. Она может быть установлена как на вашем локальном компьютере, так и располагаться удаленно, например, на VPS-сервере.

Как за 5 минут развернуть базу данных PostgreSQL на VPS-сервере, я рассказывал в этой статье: [читать статью].

Сегодня мы разберем все функции библиотеки без разбора кода самой библиотеки (то есть сегодня только практика).

Установка

Для установки последней актуальной версии библиотеки выполните команду:

pip install --upgrade asyncpg-lite

Либо воспользуйтесь requirements.txt (0.22.1 самая актуальная версия на данный момент):

asyncpg-lite~=0.22.1
pip install -r requirements.txt

Инициация класса

После установки библиотеки необходимо инициировать класс для работы с asynpg-lite, чтобы объект мог подключиться к базе данных и взаимодействовать с ней.

Импортируем библиотеку:

from asyncpg_lite import DatabaseManager

Теперь давайте подключимся к базе данных. Для удобства я предусмотрел два варианта подключения:

  • Через DSN-ссылку

  • Через данные для подключения (указывается хост, порт, имя пользователя и прочее).

Вариант инициации через DSN-ссылку

postgresql://USER_LOGIN:PASSWORD@HOST:PORT/DB_NAME

Пример использования переменных окружения для скрытия DSN-ссылки в файле .env (с использованием библиотеки python-decouple):

from asyncpg_lite import DatabaseManager
from decouple import config

pg_link = config('PG_LINK')

import asyncio

async def main():
    db_manager = DatabaseManager(dsn=pg_link)
    # Далее используем db_manager...

Вариант подключения с указанием данных для подключения

Для этого нам понадобятся:

  • dsn_flag: bool

  • host: str

  • port: int

  • user: str

  • password: str

  • database: str

Обратите внимание, что параметр dsn_flag по умолчанию равен True, что говорит классу о том, что мы используем DSN-ссылку. Для инициации класса вторым способом необходимо использовать флаг dsn_flag = False. Все остальные параметры по умолчанию установлены в None.

Настроим все параметры подключения через переменные окружения и инициируем наш класс:

from asyncpg_lite import DatabaseManager
from decouple import config


host = config('HOST')
port = config('PORT')
user = config('USER')
password = config('PASSWORD')
database = config('DB_NAME')


async def main():
    db_manager = DatabaseManager(dsn_flag=False, host=host, port=port, user=user,
                                 password=password, database=database)
    # Далее используем db_manager...

Необязательный параметр: deletion_password

По умолчанию значение этого аргумента равняется «my_root_password». Его основной смысл заключается в том, чтобы при использовании критических функций, таких как удаление таблицы или всех данных из таблицы, необходимо было явно указывать тот пароль, который был передан при инициации класса. Это сделано для защиты от случайного удаления данных и от несанкционированного доступа.

Настоятельно рекомендую указывать этот параметр всегда и тянуть его из .env.

root_pass = config('ROOT_PASS')

async def main():
    db_manager = DatabaseManager(dsn_flag=False, host=host, port=port, user=user,
                                 password=password, database=database, deletion_password=root_pass)
    # Далее используем db_manager...

Общий принцип взаимодействия с менеджером

Для работы с менеджером базы данных из asyncpg_lite необходимо использовать асинхронный контекстный менеджер with. Благодаря такому подходу у вас появляется сразу две возможности:

  1. Создавать сложные и многоуровневые запросы к базе данных PostgreSQL.

  2. Не беспокоиться о закрытии соединения с базой данных, так как оно автоматически закрывается после завершения работы с менеджером.

Конструкция подключения будет выглядеть так:

import asyncio
from asyncpg_lite import DatabaseManager
from decouple import config

async def main():
    db_manager = DatabaseManager(dsn_flag=False, host=host, port=port, user=user,
                                 password=password, database=database, deletion_password=root_pass)
    async with db_manager as manager:
        pass

asyncio.run(main())

Логи покажут:

2024-06-12 14:47:29,197 - asyncpg_lite - INFO - Соединение с базой данных установлено.
2024-06-12 14:47:29,274 - asyncpg_lite - INFO - Соединение с базой данных закрыто.

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

Функции библиотеки

Библиотека asyncpg-lite может:

  • Создавать таблицы.

  • Получать данные (возвращаются в виде списка питоновских словарей или в виде словаря, принимает фильтрацию в виде списков и словарей).

  • Добавлять данные в таблицу (принимает словарь или список словарей и производит добавление данных в указанную таблицу).

  • Добавлять данные в таблицу с обновлением данных при конфликте по первичному ключу (подробно поясню далее)

  • Изменять данные (принимает условие для выбора записей в виде словаря или списка словарей и принимает словарь с новыми данными).

  • Удалять данные (может удалять таблицу, все данные из таблицы или значения из таблицы по специальным фильтрам).

Как видите, библиотека покрывает все CRUD операции, используя только базовые типы данных из Python 3.

Создание таблицы

Для создания таблицы используйте метод create_table. Функция принимает:

  • table_name: Имя таблицы.

  • columns: Список строк, определяющих столбцы таблицы.

Пример кода для создания таблицы пользователей:

async def main():
    db_manager = DatabaseManager(dsn_flag=False, host=host, port=port, user=user,
                                 password=password, database=database, deletion_password=root_pass)
    async with db_manager as manager:
        columns = [
            "user_id INT8 PRIMARY KEY",
            "user_name VARCHAR(255)",
            "user_surname VARCHAR(255)",
            "user_age INT4"
        ]

        await manager.create_table(table_name="new_users", columns=columns)

asyncio.run(main())

Логи:

2024-06-12 15:07:46,815 - asyncpg_lite - INFO - Соединение с базой данных установлено.
2024-06-12 15:07:46,999 - asyncpg_lite - INFO - Таблица new_users успешно создана.
2024-06-12 15:07:47,152 - asyncpg_lite - INFO - Соединение с базой данных закрыто.

Добавление данных в таблицу

Для добавления данных в таблицу мы будем использовать метод insert_data. Данный метод принимает следующие параметры:

  • table_name: Имя таблицы, в которую мы будем вставлять данные

  • records_data: Словарь или список словарей с данными для вставки.

Метод автоматически определит, передан ли словарь или список словарей.

Попробуем добавить одного пользователя в нашу созданную таблицу:

async def main():
    db_manager = DatabaseManager(dsn_flag=False, host=host, port=port, user=user,
                                 password=password, database=database, deletion_password=root_pass)
    async with db_manager as manager:
        user_data = {'user_id': 1, 'user_name': 'Вася', 'user_surname': 'Сидоров', 'user_age': 40}
        await manager.insert_data(table_name="new_users", records_data=user_data)

asyncio.run(main())

Логи после выполнения:

2024-06-12 15:44:28,555 - asyncpg_lite - INFO - Соединение с базой данных установлено.
2024-06-12 15:44:28,788 - asyncpg_lite - INFO - Успешно добавлена 1 запись в таблицу new_users.
2024-06-12 15:44:28,959 - asyncpg_lite - INFO - Соединение с базой данных закрыто.

5bc7ac2ac2d33895be7008ac65790402.jpg

Видим, что все успешно обработано. Теперь попробуем добавить сразу несколько пользователей:

async def main():
    db_manager = DatabaseManager(dsn_flag=False, host=host, port=port, user=user,
                                 password=password, database=database, deletion_password=root_pass)
    async with db_manager as manager:
        user_data = [
            {'user_id': 2, 'user_name': 'Дмитрий', 'user_surname': 'Иванов', 'user_age': 30},
            {'user_id': 3, 'user_name': 'Олег', 'user_surname': 'Смирнов', 'user_age': 46},
            {'user_id': 4, 'user_name': 'Петр', 'user_surname': 'Петров', 'user_age': 23}
        ]
        await manager.insert_data(table_name="new_users", records_data=user_data)

asyncio.run(main())

Логи после выполнения:

2024-06-12 15:46:55,232 - asyncpg_lite - INFO - Соединение с базой данных установлено.
2024-06-12 15:46:55,452 - asyncpg_lite - INFO - Успешно добавлены 3 записи в таблицу new_users.
2024-06-12 15:46:55,614 - asyncpg_lite - INFO - Соединение с базой данных закрыто.

Проверим результат:

f3983e44252dbca544c09da08dfc5ae3.jpg

Видим, что данные успешно добавлены. Теперь перейдем к получению данных из таблицы.

Добавление данных в таблицу с изменением при конфликте по первичному ключу

В библиотеку asyncpg-lite добавил новый метод insert_data_with_update, который расширяет функциональность стандартного метода insert_data. Этот метод позволяет вставлять данные в таблицу базы данных и, в случае возникновения конфликта по первичному ключу, обновлять существующие записи вместо выброса ошибки.

Такой подход обеспечивает более гибкое управление данными и предотвращает прерывание процесса вставки при обнаружении дублирующих записей.

Метод принимает:

  • table_name: Имя таблицы, в которую мы будем вставлять данные

  • records_data: Словарь или список словарей с данными для вставки.

  • conflict_column: Строка с названием первичного ключа по которому может пойти конфликт

Пример использования

Предположим, у вас есть таблица users с колонками id, name и email, где id является первичным ключом. Вы хотите вставить новые записи и обновить существующие записи при конфликте по id.

# Пример использования метода insert_data_with_update
async def main():
    db_manager = DatabaseManager(dsn=pg_link)
    async with db_manager as conn:
        records = [
            {'id': 1, 'name': 'Алиса', 'email': 'alice@example.com'},
            {'id': 2, 'name': 'Боб', 'email': 'bob@example.com'},
            {'id': 3, 'name': 'Чарли', 'email': 'charlie@example.com'},
            {'id': 4, 'name': 'Алекс', 'email': 'alex@example.com'}
        ]

        await conn.insert_data_with_update('users', records, 'id')

# Вызов главной функции
asyncio.run(main())

В этом примере, если в таблице users уже существует запись с id равным 1, 2 или 3, метод insert_data_with_update обновит соответствующие строки новыми значениями из переданных данных. Если же таких записей не существует, они будут добавлены как новые.

Логи в таком случае будут показывать:

2024-06-13 14:37:58,591 - asyncpg_lite - INFO - Соединение с базой данных установлено.
2024-06-13 14:37:58,593 - asyncpg_lite - INFO - Успешно добавлено 1 запись(ей) в таблицу users.
2024-06-13 14:37:58,593 - asyncpg_lite - INFO - Успешно обновлено 3 запись(ей) в таблице users.
2024-06-13 14:37:58,605 - asyncpg_lite - INFO - Соединение с базой данных закрыто.

Получение данных

Для получения данных из таблицы используется метод select_data. Метод принимает следующие параметры:

  • table_name: Имя таблицы.

  • where_dict: Условия для фильтрации данных.

  • columns: Список столбцов для извлечения.

  • one_dict: Возвращать ли только одну запись в виде словаря.

Получим все значения пользователя с user_id равным 3:

async def main():
    db_manager = DatabaseManager(dsn_flag=False, host=host, port=port, user=user,
                                 password=password, database=database, deletion_password=root_pass)
    async with db_manager as manager:
        user_info = await manager.select_data(table_name="new_users", where_dict={'user_id': 3})
        print(user_info)

asyncio.run(main())

Логи:

2024-06-12 15:53:48,376 - asyncpg_lite - INFO - Соединение с базой данных установлено.
2024-06-12 15:53:48,600 - asyncpg_lite - INFO - Успешно получили 1 словарь из таблицы new_users.
[{'user_id': 3, 'user_name': 'Олег', 'user_surname': 'Смирнов', 'user_age': 46}]
2024-06-12 15:53:48,758 - asyncpg_lite - INFO - Соединение с базой данных закрыто.

Обратите внимание, что несмотря на запрос одного пользователя, возвращается список. Для удобства в таких случаях предусмотрен флаг one_dict, который возвращает один словарь:

user_info = await manager.select_data(table_name="new_users", where_dict={'user_id': 3}, one_dict=True)

Логи:

2024-06-12 15:56:54,972 - asyncpg_lite - INFO - Соединение с базой данных установлено.
2024-06-12 15:56:55,213 - asyncpg_lite - INFO - Успешно получили 1 словарь из таблицы new_users.
{'user_id': 3, 'user_name': 'Олег', 'user_surname': 'Смирнов', 'user_age': 46}
2024-06-12 15:56:55,367 - asyncpg_lite - INFO - Соединение с базой данных закрыто.

Видим, что возвращен один словарь. Это удобно, когда нужно получить информацию по конкретному пользователю, например, в Telegram-ботах.

Если не передавать аргумент columns, по умолчанию возвращаются все колонки. Можно передать список колонок, значения которых необходимо получить:

user_info = await manager.select_data(table_name="new_users", where_dict={'user_id': 3}, one_dict=True, columns=['user_name', 'user_surname'])

Логи:

2024-06-12 15:59:25,433 - asyncpg_lite - INFO - Соединение с базой данных установлено.
2024-06-12 15:59:25,657 - asyncpg_lite - INFO - Успешно получили 1 словарь из таблицы new_users.
{'user_name': 'Олег', 'user_surname': 'Смирнов'}
2024-06-12 15:59:25,806 - asyncpg_lite - INFO - Соединение с базой данных закрыто.

Теперь получим список всех пользователей со всеми значениями:

users_info = await manager.select_data(table_name="new_users")

Логи:

2024-06-12 16:01:12,432 - asyncpg_lite - INFO - Соединение с базой данных установлено.
2024-06-12 16:01:12,583 - asyncpg_lite - INFO - Успешно получили 4 словаря из таблицы new_users.
[{'user_id': 1, 'user_name': 'Вася', 'user_surname': 'Сидоров', 'user_age': 40}, {'user_id': 2, 'user_name': 'Дмитрий', 'user_surname': 'Иванов', 'user_age': 30}, {'user_id': 3, 'user_name': 'Олег', 'user_surname': 'Смирнов', 'user_age': 46}, {'user_id': 4, 'user_name': 'Петр', 'user_surname': 'Петров', 'user_age': 23}]
2024-06-12 16:01:12,758 - asyncpg_lite - INFO - Соединение с базой данных закрыто.

Теперь усложним запрос и получим значения имени и фамилии у тех пользователей, у которых user_id равен 1, 3 или 4:

async def main():
    db_manager = DatabaseManager(dsn_flag=False, host=host, port=port, user=user,
                                 password=password, database=database, deletion_password=root_pass)
    async with db_manager as manager:
        user_info = await manager.select_data(table_name="new_users",
                                              where_dict=[{'user_id': 1}, {'user_id': 3}, {'user_id': 4}],
                                              columns=['user_surname', 'user_name'])
        for user_data in user_info:
            print(user_data)

asyncio.run(main())

Логи:

2024-06-12 16:05:44,132 - asyncpg_lite - INFO - Соединение с базой данных установлено.
2024-06-12 16:05:44,349 - asyncpg_lite - INFO - Успешно получили 3 словаря из таблицы new_users.
{'user_surname': 'Сидоров', 'user_name': 'Вася'}
{'user_surname': 'Смирнов', 'user_name': 'Олег'}
{'user_surname': 'Петров', 'user_name': 'Петр'}
2024-06-12 16:05:44,506 - asyncpg_lite - INFO - Соединение с базой данных закрыто.

Изменение данных

Для изменения данных в библиотеке asyncpg-lite я предусмотрел универсальный метод update_data. Данный метод принимает следующие параметры:

  • table_name: Имя таблицы.

  • where_dict: Условия для выбора записей для обновления (словарь или список словарей).

  • update_dict: Словарь с данными для обновления или строка.

Давайте посмотрим на пример использования:

async def main():
    db_manager = DatabaseManager(dsn_flag=False, host=host, port=port, user=user,
                                 password=password, database=database, deletion_password=root_pass)
    async with db_manager as manager:
        # Пример одиночного словаря
        await manager.update_data(
            table_name="new_users",
            where_dict={'user_id': 2},
            update_dict={'user_name': 'Иван', 'user_age': 41}
        )

        # Пример списка словарей
        await manager.update_data(
            table_name="new_users",
            where_dict=[{'user_id': 2}, {'user_id': 3}],
            update_dict={'user_name': 'Иван', 'user_age': 41}
        )

Логи:

2024-06-12 16:13:14,391 - asyncpg_lite - INFO - Соединение с базой данных установлено.
2024-06-12 16:13:14,616 - asyncpg_lite - INFO - Записи успешно обновлены.
2024-06-12 16:13:14,918 - asyncpg_lite - INFO - Записи успешно обновлены.
2024-06-12 16:13:15,065 - asyncpg_lite - INFO - Соединение с базой данных закрыто.

Обратите внимание: я использовал метод не просто одиночно, а применил серийную обработку данных. На этом примере можно увидеть преимущество использования подхода работы через async with. Теперь давайте разберемся в коде и его возможностях.

Пример одиночного словаря

await manager.update_data(
    table_name="new_users",
    where_dict={'user_id': 2},
    update_dict={'user_name': 'Иван', 'user_age': 41}
)

Здесь мы передали в where_dict одиночный словарь с параметрами фильтрации, а в update_dict — словарь с новыми значениями. Иногда бывает необходимо фильтровать по нескольким значениям одного и того же параметра. Именно для таких случаев я добавил возможность указания в where_dict списка словарей.

Пример списка словарей

await manager.update_data(
    table_name="new_users",
    where_dict=[{'user_id': 2}, {'user_id': 3}],
    update_dict={'user_name': 'Иван', 'user_age': 41}
)

Когда это может быть полезно?

Например, в вашем телеграмм-боте нужно заблокировать нескольких пользователей. Через админку вы просто передаете список пользователей, и данный подход обходит каждого и изменяет по ним данные. Код разработан таким образом, чтобы одной функцией можно было обработать неограниченное количество значений.

Удаление данных

Как я уже описывал выше, в моей библиотеке предусмотрены три формата удаления данных:

  • Удаление записей из таблицы по параметрам (метод delete_data).

  • Удаление всех записей из таблицы (метод delete_all_data).

  • Удаление таблицы (метод drop_table).

Метод delete_data

Метод delete_data принимает следующие параметры:

  • table_name: Имя таблицы.

  • where_dict: Условия для выбора записей для удаления (словарь или список словарей).

Давайте удалим из таблицы пользователя с user_id = 1:

await manager.delete_data('new_users', {'user_id': 1})

Логи:

2024-06-12 16:27:22,760 - asyncpg_lite - INFO - Соединение с базой данных установлено.
2024-06-12 16:27:22,992 - asyncpg_lite - INFO - Записи успешно удалены.
2024-06-12 16:27:23,152 - asyncpg_lite - INFO - Соединение с базой данных закрыто.

Проверяем:

0506eb5f4ea4c885ffda9c9eb3cf3f53.jpg

Видим, что запись удалена.

Теперь удалим пользователей с user_id = 2 и 3:

await manager.delete_data('new_users', [{'user_id': 2}, {'user_id': 3}])

Запускаем и смотрим логи:

2024-06-12 16:35:20,132 - asyncpg_lite - INFO - Соединение с базой данных установлено.
2024-06-12 16:35:20,349 - asyncpg_lite - INFO - Записи успешно удалены.
2024-06-12 16:35:20,506 - asyncpg_lite - INFO - Соединение с базой данных закрыто.

Смотрим в таблицу:

31fb22150ff3e71f69003b427f3e910c.jpg

Видим, что в таблице остался всего один пользователь. Его мы удалим при помощи метода delete_all_data. Этот метод принимает:

  • table_name: Имя таблицы.

  • password: Пароль, который должен совпадать с проверочным паролем, указанным при инициализации менеджера.

Код:

await manager.delete_all_data('new_users', password=root_password)

Логи:

2024-06-12 16:40:34,285 - asyncpg_lite - INFO - Соединение с базой данных установлено.
2024-06-12 16:40:34,434 - asyncpg_lite - INFO - Все записи успешно удалены.
2024-06-12 16:40:34,591 - asyncpg_lite - INFO - Соединение с базой данных закрыто.

Вся информация успешно удалена.

Метод drop_table принимает те же данные, что и delete_all_data, но удаляет таблицу.

Реализация на практике

Теперь, когда мы полностью знакомы с методами из библиотеки asyncpg-lite, давайте реализуем более наглядные примеры использования этих методов.

Для удобства пользования библиотекой создадим несколько функций, которые могут быть использованы в реальных примерах. Для начала создадим новую таблицу пользователей с именем fake_users при помощи метода create_table.

Создание таблицы

async def main():
    db_manager = DatabaseManager(dsn=pg_link, deletion_password=password)
    async with db_manager as manager:
        columns = [
            "user_id INT4 PRIMARY KEY",
            "name VARCHAR(100)",
            "address VARCHAR(255)",
            "email VARCHAR(100)",
            "phone_number VARCHAR(20)",
            "birth_date DATE",
            "company VARCHAR(100)",
            "job VARCHAR(100)"
        ]
        await manager.create_table('fake_users', columns)

Выполняем код и видим, что таблица создана.

Теперь напишем простую функцию, основанную на библиотеке Faker, которая будет генерировать пользователей.

Генерация пользователей

from faker import Faker

def generate_users(count: int, start_id: int = 1):
    # Создаем объект Faker с русской локализацией
    fake = Faker('ru_RU')

    # Список для хранения сгенерированных пользователей
    users = []

    # Генерация пользователей
    for i in range(count):
        user_id = start_id + i
        user = {
            'user_id': user_id,
            'name': fake.name(),
            'address': fake.address(),
            'email': fake.email(),
            'phone_number': fake.phone_number(),
            'birth_date': fake.date_of_birth(),
            'company': fake.company(),
            'job': fake.job()
        }
        users.append(user)

    return users

Эта замечательная библиотека придумает за нас все данные. Единственное, на что нужно обратить внимание, — это user_id, который не должен дублироваться, так как он является первичным ключом. В своем коде я это обошел при помощи цикла for. Также вы можете передавать стартовый идентификатор нового пользователя.

Добавление пользователей

Давайте, используя эту функцию, добавим в нашу созданную таблицу 5 пользователей. Выше я подробно описывал, как работает метод insert_data.

Импортируем функцию по генерации пользователей:

from utils.utils import generate_users

Пишем код:

async def main():
    db_manager = DatabaseManager(dsn=pg_link, deletion_password=password)
    async with db_manager as manager:
        users_info = generate_users(count=5, start_id=1)
        await manager.insert_data('fake_users', users_info)

Смотрим логи:

2024-06-12 17:13:20,379 - asyncpg_lite - INFO - Соединение с базой данных установлено.
2024-06-12 17:13:21,819 - asyncpg_lite - INFO - Успешно добавлено 5 записей в таблицу fake_users.
2024-06-12 17:13:21,990 - asyncpg_lite - INFO - Соединение с базой данных закрыто.

Проверяем:

3b142c7ee186730a3ed4e372e3c3c3c2.jpg

Пользователи добавлены!

Теперь, для закрепления пройденного материала, выполним многоуровневый запрос. Для начала удалим всю таблицу:

await manager.drop_table('fake_users', password=root_password)

Таблица удалена.

Теперь давайте создадим такой запрос:

  1. Генерируем таблицу fake_users.

  2. Добавляем в таблицу 10 пользователей (айди начиная с 1).

  3. Изменяем значение колонки job у пользователей с четным ID на «безработный».

  4. Удаляем двух пользователей с ID 9 и 7.

  5. Возвращаем всех оставшихся пользователей через цикл for.

Перед тем как начать писать код, попробуйте реализовать задачу самостоятельно. Моя реализация:

Hidden text

async def main():
    db_manager = DatabaseManager(dsn=pg_link, deletion_password=password)
    async with db_manager as manager:
        # Создаем таблицу 'fake_user'
        await manager.create_table('fake_user', columns=[
            "user_id INT4 PRIMARY KEY",
            "name VARCHAR(100)",
            "address VARCHAR(255)",
            "email VARCHAR(100)",
            "phone_number VARCHAR(20)",
            "birth_date DATE",
            "company VARCHAR(100)",
            "job VARCHAR(100)"
        ])

        # Создаем 10 пользователей
        users_data = generate_users(count=10, start_id=1)

        # Добавляем пользователей в базу данных
        await manager.insert_data('fake_user', users_data)

        # Создаем список словарей с пользователями, которых уволим
        even_upd_dict_list = []
        for i in users_data:
            user_id = i.get('user_id')
            if user_id % 2 == 0:
                even_upd_dict_list.append({'user_id': user_id})

        # Увольняем пользователей, у которых user_id - четное число
        await manager.update_data('fake_user', where_dict=even_upd_dict_list, update_dict={'job': 'безработный'})

        # Удаляем пользователей с user_id 9 и 7
        await manager.delete_data('fake_user', [{'user_id': 9}, {'user_id': 7}])

        # Возвращаем оставшихся пользователей
        now_users = await manager.select_data('fake_user')

        for i in now_users:
            print(i)

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

Мой результат

Мой результат

С помощью подхода, который я выбрал в реализации библиотеки, вы сможете создавать сколько угодно сложные сценарии взаимодействия со своими таблицами.

Заключение

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

Ребята, которые имеют большой опыт в разработке и во взаимодействии с PostgreSQL через Python, кто хочет также создать что-то полезное для всех, — выходите со мной на связь любым доступным способом.

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

У меня все. Надеюсь, что проект asyncpg-lite будет развиваться дальше. Спасибо за внимание и жду обратной связи по библиотеке, надеюсь, приятной.

PS. За последние два дня было несколько обновлений библиотеки. Среди последних значимых изменений — внедрение метода insert_data_with_update. Если вы хотите получать информацию о новых обновлениях первыми — подписывайтесь. Дальше, кроме всего прочего, я планирую делать небольшие посты на Хабре, где буду рассказывать о новых функциях и улучшениях библиотеки asyncpg-lite.

© Habrahabr.ru