Как стать автором
Обновить

Мелкая питонячая радость #8: мелкие удовольствия для работы с БД

Время на прочтение 3 мин
Количество просмотров 16K

Беглый опрос коллег на моем текущем проекте показал, что при словах "ORM и работа с БД" в подавляющем большинстве случаев звучат слова "Алхимия" и "Django ORM". Знания этих двух слов, в общем, достаточно, чтобы писать чистый, аккуратный и рабочий код. Но расширение инженерного кругозора пока еще никому не вредило, поэтому сегодня мы добавим в нашу картину мира несколько (возможно, до этого дня незнакомых) классных штук для работы с БД.



Yoyo


Сегодня любая ORM идет со внутренней системой миграции БД. Простой и классный подход, когда ORM следит за структурой таблиц, в общем, всех устраивает. Но у этого решения случая, когда пользоваться им неудобно:


  • Есть парни, которые фигачат запросы в БД, минуя ORM. При этом используется что-то типа asyncpg и небольшая самописная нашлепка для упрощения составления запросов.


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


  • Встроенные миграторы могут иметь свои специфические взгляды на построение связей и индексирование записей. Порой победить эти воззрения ORM на структуру таблиц не удается, приходится самому нашлепывать исправления запросов.

В обоих этих случаях удобно примененять миграции, составленных вручную — мы не опираемся на модели ORM, а втупую руками набиваем необходимые SQL инструкции и кормим их в простенький мигратор, который последовательно применяет изменения структуры к БД.


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


Для такого вот ручного подхода существует мигратор схем БД yoyo.


pip install yoyo-migrations

Дальше вся работа по управлению миграциями идет с помощью исполняемого файла yoyo


yoyo new ./migrations -m "Add column to foo"

Команда создает файлик, в который можно вписать одну или несколько инструкций для миграции схемы


from yoyo import step
steps = [
   step("CREATE TABLE foo (id INT, bar VARCHAR(20), PRIMARY KEY (id))",
        "DROP TABLE foo"),
]

После этого миграции можно катить на базу


yoyo apply --database postgresql://scott:tiger@localhost/db ./migrations

Все просто, как полено. И при этом у вас абсолютно полный контроль над тем, как выглядит БД.


Минусов у такого подхода два


  • За набором полей в таблицах и их параметрами придется следить ручками. Каждое изменение приводит к необходимости писать ALTER TABLE самому, теряется возможность мигрировать все в один клик.
  • Мерджить такие миграции тоже придется всегда руками и головой. Это, конечно, лишний труд. Но на практике конфликты и сложные мерджи миграций встречаются редко.

Peewee


image


Маленькая и не самая популярная ORM (хотя про нее писали здесь уже не один раз), которая, тем не менее, имеет свою аудиторию.
Peewee создана быть максимально тупой и простой оберткой для БД, с максимально понятным механизмом выполнения запросов и легко читаемым кодом.


Users.select().where(
   Users.user_id == user_id
).get()

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


  • Адекватная производительность (хотя и не самая большая скорость выполнения запросов)
  • Все необходимые плюшки — различные наборы полей, связи между сущностями, пулы коннектов, наборы плагинов и расширений.
  • Есть даже более-менее вменяемая асинхронщина, добавляемая сторонним модулем peewee_async.

Pony ORM


Pony — легендарная своей скоростью обертка. Слой работы с БД, написанный с помощью этой ORM, может по скорости в разы надрать зад другим решениям. Магии в ее скорости нет, есть очень грамотная политика кеширования запросов в базы, кучи оптимизаций и хитрости с кодом. В сумме это приводит к тому что Пони жарит с конской скоростью.


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


query = select(c for c in Customer
               if sum(o.total_price for o in c.orders) > 1000)

Product.select().order_by(lambda p: desc(sum(p.order_items.quantity))).first()

Такой подход требует определенного слома мозга.


Tortoise ORM



Копаясь в разных питонячих репо, я нашел сборник тестов разных ORM с замерами по скорости. Помимо уже упомянутой Pony ORM в списке самых шустрых оказалась некая Tortoise ORM. Понятное дело, что результаты тесты зависят от того, кто тесты пишет и как их потом запускает, но посмотреть поближе на эту штуку надо.


Tortoise — относительно молодой проект, который пока что находится в стадии активной разработки. Хорошая производительность этой библиотеки объясняется тем, что ORM не содержит ниего лишнего и из коробки заточена под асинхронщину. А еще в ней предполагается использование uvloop, который работает быстрее, чем родные питонячие циклы эвентов.


ORM эта еще слишком сыра для применения в бою (например, пока даже не реализованы пулы коннектов), но посматривать за развитием этой либы стоит. Если у разработчиков все пойдет нормально — в ближайший год мы получим действительно быструю обертку для БД с хорошей скоростью и без странного синтаксиса в стиле Pony.

Теги:
Хабы:
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
+15
Комментарии 16
Комментарии Комментарии 16

Публикации

Истории

Работа

Python разработчик
136 вакансий
Data Scientist
66 вакансий

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн