TGViewer
Советы разработчикам (python и не только) Советы разработчикам (python и не только) @advice17 · 8.35K subscribers
Post #74 14K
SQLAlchemy и ORM

SQLAlchemy в python предоставляет два набора API:

1. Возможность конструировать и выполнять запросы в БД
2. Получать и сохранять сущности

Первый набор api (Core функциональность) - это в первую очередь объекты запросов select, insert, update и других, а также метод execute, который есть у объекта Сonnection (можно использовать Session, но о нем ниже). Часто при этом используются объекты Table для описания структуры БД.

Кроме этого, sqlalchemy можно использовать как ORM. Это означает, что вы создаете классы, описывающие ваши сущности, а затем все операции с БД делаете только через них: вы загружаете сущности, сохраняете сущности, удаляете сущности (см. пост про стейт приложения и БД). При этом Connection вам уже недостаточно, вам требуется Session.

В чем ключевые отличия ORM подхода от Core?

• Для загрузки сущностей используется session.get для получения по первичному ключу или select(Model) запрос в других случаях. Обратите внимание, что речь идет именно о загрузке моделей целиком, а не отдельных полей таблиц. Кроме того, сессия реализует паттерн IdentityMap, то есть если объект уже загружен в память, то в некоторых случаях повторный запрос в БД не требуется (при get или загрузке связанных моделей), а если БД вернет одну сущность несколько раз, будет переиспользован один экземпляр.
• Для добавления новых сущностей мы используем метод session.add. Сессия реализует паттерн Unit of Work, поэтому все добавленные сущности будут хранится в промежуточном буфере и сохранятся при flush или commit. Если сущности содержат связи, они тоже будет автоматически добавлены. При чём алхимия следит за правильным порядком вставки и даже пытается оптимизировать запросы.
• Для применения изменений надо просто поменять сущности и закоммитить транзакцию. Опять же, благодаря Unit of work все изменения отслеживаются автоматически и не требуется вызывать никаких методов сохранения на каждом экземпляре вручную.
• Для удаления есть отдельный метод UoW - session.delete. Кроме непосредственно удаления сущности он так же следит за связями и обрабатывает их, зачастую более сложным способом чем умеет сама СУБД.

Таким образом, если вы используете модели алхимии, но вызываете insert или update, вы фактически не используете ORM. Так можно делать в целях оптимизации в отдельных нагруженных частях программы, но оно не должно восприниматься как основной способ работы с ORM.

Другой ошибкой является создание отдельных доменных сущностей одновременно с моделями ORM. Из-за этого приходится писать достаточно сложные мапперы, которые часто не учитывают наличие IdM и UoW, что может привести к некорректной логике сохранения данных. Вместо этого можно выделить три подхода:

Не использовать ORM. Пишите сущности как вам удобно и сохраняйте в БД используя core-функциональность. Вы лишаетесь встроенного UoW, но и не конфликтуете с ним.
Использовать модели ORM как бизнес сущности. Ваши сущности выглядят чуть более грязно, однако это только метаданные, которые в БЛ использоваться не будут.
Использовать датаклассы и императивный маппинг, описывая отдельно Table объекты. Вы все ещё ограничены тем, что алхимия меняет модели, внедряя скрытые атрибуты, но ваша БЛ уже это не видит. Вы не можете использовать совсем кастомные мапперы для отдельных моделей, но зато имеете все возможности алхимии.

Даже используя ORM приходится писать сложные select запросы для реализации поиска или получения сводной информации, поэтому имеет смысл реализовать паттерн репозиторий, скрывая их построение внутри, но при этом мы все ещё полагаемся на возможности сессии как UoW.

Кроме того, в нашем приложении кроме логики работы с сущностями могут существовать запросы чтения, возвращающие клиенту определенный срез данных. Для этого мы можем реализовать простой DAO, использующий Core алхимии или описать альтернативные модели, которые будут императивно маппиться на те же таблицы, но иметь другую структуру.

Дополнительные материалы:
Mapping SQLAlchemy to dataclass
Mapping a Class against Multiple Tables
Patterns Implemented by SQLAlchemy
  • 👍 108
  • 🤡 27
  • 🔥 14
  • ❤ 10
  • 🤓 5
  • ❤‍🔥 3
  • 🤔 2
  • 🐳 2
  • 👎 1
  • 🤝 1
More from @advice17
  1. May 31, 2026Юзкейсы, сценарии и интеракторы Когда мы пишем наш код (программу, сервис, библиотеку), мы…
  2. Feb 17, 2026Меня позвали поговорить о разном: о проектах, опыте, программировании в целом. https://www…
  3. Nov 4, 2025Asyncio и колбэки В прошлом посте мы рассмотрели как asyncio взаимодействует с генераторам…
  4. Sep 27, 2025Стейт при работе с базами данных Подходы работы с БД можно разделить на две группы: • Орие…
  5. Aug 7, 2025Asyncio и цикл событий Чтобы понять, как рабоатет asyncio, давайте рассмотрим, как в Pytho…
  6. Apr 2, 2025Anti corruption layer Часто в наших приложениях мы обращаемся к каким-то внешним системам.…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →