1 added
1 removed
Original
2026-01-01
Modified
2026-02-26
1
<p><strong>Рассказываем про обновление опенсорс-проекта SQLAlchemy, с помощью которого миллионы Python-разработчиков работают с базами данных.</strong></p>
1
<p><strong>Рассказываем про обновление опенсорс-проекта SQLAlchemy, с помощью которого миллионы Python-разработчиков работают с базами данных.</strong></p>
2
<p>В начале 2023 года вышла SQLAlchemy 2.0 - библиотека на Python для работы с реляционными СУБД, которая работает с Object Relational Mapper (объектно-реляционным отображением). Основная задача SQLAlchemy - синхронизация объектов Python с данными в БД.</p>
2
<p>В начале 2023 года вышла SQLAlchemy 2.0 - библиотека на Python для работы с реляционными СУБД, которая работает с Object Relational Mapper (объектно-реляционным отображением). Основная задача SQLAlchemy - синхронизация объектов Python с данными в БД.</p>
3
<p>То есть с помощью SQLAlchemy можно описывать структуры БД и работать с их данными на объектно-ориентированном коде на Python без использования чистого SQL. Другая важная особенность SQLAlchemy - код для работы с базой данных будет одинаковым вне зависимости от БД, которую использует разработчик. Такой подход позволяет без проблем мигрировать с одной базой данных на другую.</p>
3
<p>То есть с помощью SQLAlchemy можно описывать структуры БД и работать с их данными на объектно-ориентированном коде на Python без использования чистого SQL. Другая важная особенность SQLAlchemy - код для работы с базой данных будет одинаковым вне зависимости от БД, которую использует разработчик. Такой подход позволяет без проблем мигрировать с одной базой данных на другую.</p>
4
<p>Полный список нововведений в SQLAlchemy 2.0 можно посмотреть в<a>официальной</a>документации сервиса.</p>
4
<p>Полный список нововведений в SQLAlchemy 2.0 можно посмотреть в<a>официальной</a>документации сервиса.</p>
5
<blockquote><h3>Читайте также:</h3>
5
<blockquote><h3>Читайте также:</h3>
6
<p>Программирование на Python:<a>особенности обучения</a>, перспективы, ситуация на рынке труда</p>
6
<p>Программирование на Python:<a>особенности обучения</a>, перспективы, ситуация на рынке труда</p>
7
</blockquote><h2>Содержание</h2>
7
</blockquote><h2>Содержание</h2>
8
<ul><li><a>Новый интерфейс запросов</a></li>
8
<ul><li><a>Новый интерфейс запросов</a></li>
9
<li><a>Обновленные менеджеры контекста</a></li>
9
<li><a>Обновленные менеджеры контекста</a></li>
10
<li><a>Аннотации типов</a></li>
10
<li><a>Аннотации типов</a></li>
11
<li><a>Отношения Write-Only</a></li>
11
<li><a>Отношения Write-Only</a></li>
12
<li><a>Поддержка асинхронности</a></li>
12
<li><a>Поддержка асинхронности</a></li>
13
</ul><h2>Новый интерфейс запросов</h2>
13
</ul><h2>Новый интерфейс запросов</h2>
14
<p>В SQLAlchemy 2.0 появился новый интерфейс запросов. Если быть точным, эта функция была представлена в релизе SQLAlchemy 1.4 как способ помочь разработчикам перейти на версию 2.0.</p>
14
<p>В SQLAlchemy 2.0 появился новый интерфейс запросов. Если быть точным, эта функция была представлена в релизе SQLAlchemy 1.4 как способ помочь разработчикам перейти на версию 2.0.</p>
15
<p>До этого основной (но теперь уже устаревший) способ выполнения запросов в ORM SQLAlchemy заключался в использовании объекта Query, доступного из метода Session.query(). Либо метода Model.query, если разработчик использовал расширение Flask-SQLAlchemy для микрофреймворка Flask.</p>
15
<p>До этого основной (но теперь уже устаревший) способ выполнения запросов в ORM SQLAlchemy заключался в использовании объекта Query, доступного из метода Session.query(). Либо метода Model.query, если разработчик использовал расширение Flask-SQLAlchemy для микрофреймворка Flask.</p>
16
<p>В релизе SQLAlchemy 2.0 это теперь считается устаревшим способом выполнения запросов. Разработчики по-прежнему могут делать запросы этими методами, но в документации к SQLAlchemy такой подход уже называется "API запросов 1.x" или "устаревший API запросов".</p>
16
<p>В релизе SQLAlchemy 2.0 это теперь считается устаревшим способом выполнения запросов. Разработчики по-прежнему могут делать запросы этими методами, но в документации к SQLAlchemy такой подход уже называется "API запросов 1.x" или "устаревший API запросов".</p>
17
<p>Новый Query API имеет четкое разделение между самими запросами и средой выполнения, в которой они выполняются. Приведенный выше запрос для поиска пользователя по атрибуту username теперь можно записать вот так:</p>
17
<p>Новый Query API имеет четкое разделение между самими запросами и средой выполнения, в которой они выполняются. Приведенный выше запрос для поиска пользователя по атрибуту username теперь можно записать вот так:</p>
18
<p>В этом примере запрос сохраняется в переменной query. При этом сейчас запрос еще не выполнен и даже пока не связан с сеансом. Для выполнения этого запроса его нужно передать в метод execute() объекта сеанса:</p>
18
<p>В этом примере запрос сохраняется в переменной query. При этом сейчас запрос еще не выполнен и даже пока не связан с сеансом. Для выполнения этого запроса его нужно передать в метод execute() объекта сеанса:</p>
19
<p>Возвращаемое значение из execute() - это объект Result, который функционирует как итерируемый объект, возвращающий объекты Row с интерфейсом как у кортежа. При этом в самом Python<a>нет функции с таким названием</a>, под Row обычно понимают "строку" или "запись" в контексте работы с базами данных. Если же разработчик хочет получить результаты, не дублируя их, есть несколько методов, которые можно вызвать для этого объекта:</p>
19
<p>Возвращаемое значение из execute() - это объект Result, который функционирует как итерируемый объект, возвращающий объекты Row с интерфейсом как у кортежа. При этом в самом Python<a>нет функции с таким названием</a>, под Row обычно понимают "строку" или "запись" в контексте работы с базами данных. Если же разработчик хочет получить результаты, не дублируя их, есть несколько методов, которые можно вызвать для этого объекта:</p>
20
<ul><li>Метод all() позволяет вернуть list с объектом строки для каждой строки результата</li>
20
<ul><li>Метод all() позволяет вернуть list с объектом строки для каждой строки результата</li>
21
<li>Метод first() вернет первую строку результата</li>
21
<li>Метод first() вернет первую строку результата</li>
22
<li>Метод one() вернет первую строку результата и вызовет исключение, если в ответе нет результата. Либо в объекте есть несколько одинаковых объектов, которые подходят под результат</li>
22
<li>Метод one() вернет первую строку результата и вызовет исключение, если в ответе нет результата. Либо в объекте есть несколько одинаковых объектов, которые подходят под результат</li>
23
<li>Метод one_or_none() вернет первую строку результата, None - если результатов нет, или вызовет исключение, если есть более чем один подходящий к результату объект.</li>
23
<li>Метод one_or_none() вернет первую строку результата, None - если результатов нет, или вызовет исключение, если есть более чем один подходящий к результату объект.</li>
24
</ul><p>Работа с результатами в виде кортежей имеет смысл, когда каждая строка результата может содержать несколько подходящих значений. Однако, когда у нас есть только одно значение на строку, то извлекать данные из одноэлементных кортежей может быть достаточно утомительно. Поэтому новый интерфейс имеет два дополнительных метода, которые делают работу со строками с одним значением более удобной:</p>
24
</ul><p>Работа с результатами в виде кортежей имеет смысл, когда каждая строка результата может содержать несколько подходящих значений. Однако, когда у нас есть только одно значение на строку, то извлекать данные из одноэлементных кортежей может быть достаточно утомительно. Поэтому новый интерфейс имеет два дополнительных метода, которые делают работу со строками с одним значением более удобной:</p>
25
<ul><li>Метод scalars() возвращает ScalarResult объект с первым значением каждой строки результата. И перечисленные выше методы остаются доступны для этого нового объекта результата.</li>
25
<ul><li>Метод scalars() возвращает ScalarResult объект с первым значением каждой строки результата. И перечисленные выше методы остаются доступны для этого нового объекта результата.</li>
26
<li>Метод scalar() возвращает первое значение первой строки результата.</li>
26
<li>Метод scalar() возвращает первое значение первой строки результата.</li>
27
</ul><p>Поэтому устаревший запрос может быть теперь выполнен вот так:</p>
27
</ul><p>Поэтому устаревший запрос может быть теперь выполнен вот так:</p>
28
<h2>Обновленные менеджеры контекста</h2>
28
<h2>Обновленные менеджеры контекста</h2>
29
<p>Менеджеры контекста сеанса впервые появились, как и интерфейс запросов, еще в SQLAlchemy 1.4. Но теперь этот подход стал стандартом, который используется в SQLAlchemy.</p>
29
<p>Менеджеры контекста сеанса впервые появились, как и интерфейс запросов, еще в SQLAlchemy 1.4. Но теперь этот подход стал стандартом, который используется в SQLAlchemy.</p>
30
<p>Раньше сеанс с локальной областью видимости был основным шаблоном для работы с сеансами. Расширение Flask-SQLAlchemy, например, включило его в переменную db.session, которая является его сигнатурой. То есть раньше сеанс имел ограниченную зону видимости и привязывался к потоку. Даже несмотря на то, что условная жизнь сеанса может быть намного короче, чем потока - и лечилось это только ручным управлением.</p>
30
<p>Раньше сеанс с локальной областью видимости был основным шаблоном для работы с сеансами. Расширение Flask-SQLAlchemy, например, включило его в переменную db.session, которая является его сигнатурой. То есть раньше сеанс имел ограниченную зону видимости и привязывался к потоку. Даже несмотря на то, что условная жизнь сеанса может быть намного короче, чем потока - и лечилось это только ручным управлением.</p>
31
<p>Теперь сеанс можно инициировать с помощью менеджера контекста, что позволяет прозрачно наблюдать за началом сеанса и его концом:</p>
31
<p>Теперь сеанс можно инициировать с помощью менеджера контекста, что позволяет прозрачно наблюдать за началом сеанса и его концом:</p>
32
<p>Здесь сеанс закрывается, когда заканчивается блок диспетчера контекста. И, если внутри него возникает ошибка, то сессия откатывается.</p>
32
<p>Здесь сеанс закрывается, когда заканчивается блок диспетчера контекста. И, если внутри него возникает ошибка, то сессия откатывается.</p>
33
<p>Вариант этого шаблона можно использовать для сеанса, который автоматически фиксируется в конце, но все еще откатывается при ошибках:</p>
33
<p>Вариант этого шаблона можно использовать для сеанса, который автоматически фиксируется в конце, но все еще откатывается при ошибках:</p>
34
<h2>Аннотации типов</h2>
34
<h2>Аннотации типов</h2>
35
<p>Еще одно интересное изменение, представленное в версии SQLAlchemy 2.0, - это возможность использовать подсказки при вводе данных во время объявления столбцов и связей в моделях. По сути, это введение элементов строгой типизации. Рассмотрим это на модели User:</p>
35
<p>Еще одно интересное изменение, представленное в версии SQLAlchemy 2.0, - это возможность использовать подсказки при вводе данных во время объявления столбцов и связей в моделях. По сути, это введение элементов строгой типизации. Рассмотрим это на модели User:</p>
36
<p>В версии 2.0 тип столбца можно определить с помощью Mapped-подсказки. Если есть какие-то дополнительные опции, их можно указать в mapped_column().</p>
36
<p>В версии 2.0 тип столбца можно определить с помощью Mapped-подсказки. Если есть какие-то дополнительные опции, их можно указать в mapped_column().</p>
37
<p>Применение подсказок для типов данных может дать разработчикам несколько преимуществ:</p>
37
<p>Применение подсказок для типов данных может дать разработчикам несколько преимуществ:</p>
38
<ul><li>При использовании IDE, которая выполняет статический анализ кода и предлагает изменения по мере ввода, строго типизированная модель поможет вашей IDE лучше понять код.</li>
38
<ul><li>При использовании IDE, которая выполняет статический анализ кода и предлагает изменения по мере ввода, строго типизированная модель поможет вашей IDE лучше понять код.</li>
39
<li>Это позволяет проще работать с dataclasses, которые также опираются на выбранный разработчиком тип данных.</li>
39
<li>Это позволяет проще работать с dataclasses, которые также опираются на выбранный разработчиком тип данных.</li>
40
<li>Это приводит к тому, что из SQLAlchemy теперь нужно импортировать меньше символов. Потому что теперь для столбцов, которые принимают данные только в числах, датах, времени или даже UUID, можно указывать формат в виде сразу в виде подсказки.</li>
40
<li>Это приводит к тому, что из SQLAlchemy теперь нужно импортировать меньше символов. Потому что теперь для столбцов, которые принимают данные только в числах, датах, времени или даже UUID, можно указывать формат в виде сразу в виде подсказки.</li>
41
-
</ul><p>Как и в случае с запросами, SQLAlchemy по-прежнему подд��рживает старый способ определения столбцов и связей.</p>
41
+
</ul><p>Как и в случае с запросами, SQLAlchemy по-прежнему поддерживает старый способ определения столбцов и связей.</p>
42
<blockquote><h3>Читайте также:</h3>
42
<blockquote><h3>Читайте также:</h3>
43
<p>Как создатель Python Гвидо ван Россум устроился в Microsoft и теперь работает над<a>развитием CPython</a></p>
43
<p>Как создатель Python Гвидо ван Россум устроился в Microsoft и теперь работает над<a>развитием CPython</a></p>
44
</blockquote><h2>Отношения Write-Only</h2>
44
</blockquote><h2>Отношения Write-Only</h2>
45
<p>Динамические отношения считаются устаревшими в SQLAlchemy 2.0, поскольку они несовместимы с новым интерфейсом запросов. Вместо этого рекомендуемым решением является новый тип отношений под названием Write-Only ("Только для записи"). Вот как определить отношения только для записи:</p>
45
<p>Динамические отношения считаются устаревшими в SQLAlchemy 2.0, поскольку они несовместимы с новым интерфейсом запросов. Вместо этого рекомендуемым решением является новый тип отношений под названием Write-Only ("Только для записи"). Вот как определить отношения только для записи:</p>
46
<p>Отличие от старой динамической связи в том, что связь Write-Only не загружает и не читает связанные объекты. Она только предоставляет методы add() и remove() для внесения изменений или для записи в них.</p>
46
<p>Отличие от старой динамической связи в том, что связь Write-Only не загружает и не читает связанные объекты. Она только предоставляет методы add() и remove() для внесения изменений или для записи в них.</p>
47
<p>С таким типом отношений можно получать связанные объекты через метод select(), который возвращает запрос, его можно выполнить в сеансе - например, после добавления фильтров, сортировки или разбивки на страницы.</p>
47
<p>С таким типом отношений можно получать связанные объекты через метод select(), который возвращает запрос, его можно выполнить в сеансе - например, после добавления фильтров, сортировки или разбивки на страницы.</p>
48
<p>Вот пример того, как получить объекты tokens, связанные с пользователем, и отсортированные по сроку их действия:</p>
48
<p>Вот пример того, как получить объекты tokens, связанные с пользователем, и отсортированные по сроку их действия:</p>
49
<h2>Поддержка асинхронности</h2>
49
<h2>Поддержка асинхронности</h2>
50
<p>SQLAlchemy 1.4 представила бета-версию расширения asyncio, которая показала асинхронные версии объектов Engine и Session. В версии 2.0 это расширение больше не считается бета-версией.</p>
50
<p>SQLAlchemy 1.4 представила бета-версию расширения asyncio, которая показала асинхронные версии объектов Engine и Session. В версии 2.0 это расширение больше не считается бета-версией.</p>
51
<p>Большая часть настройки асинхронного решения в SQLAlchemy включает в себя предотвращение всех способов неявной работы с БД для того, чтобы минимизировать количество возможных ошибок. Поэтому для использования расширения asyncio нужно, чтобы разработчик хотя бы базово понимал, как работает asyncio под капотом и умел разбираться над выводом ошибок. Подробнее про асинхронность в SQLAlchemy можно почитать<a>тут</a>.</p>
51
<p>Большая часть настройки асинхронного решения в SQLAlchemy включает в себя предотвращение всех способов неявной работы с БД для того, чтобы минимизировать количество возможных ошибок. Поэтому для использования расширения asyncio нужно, чтобы разработчик хотя бы базово понимал, как работает asyncio под капотом и умел разбираться над выводом ошибок. Подробнее про асинхронность в SQLAlchemy можно почитать<a>тут</a>.</p>
52
<p><em>Этот текст - адаптированный<a>перевод</a>материала What's New in SQLAlchemy 2.0? с сайта blog.miguelgrinberg.com</em></p>
52
<p><em>Этот текст - адаптированный<a>перевод</a>материала What's New in SQLAlchemy 2.0? с сайта blog.miguelgrinberg.com</em></p>
53
<blockquote><h3>Изучите Python на Хекслете</h3>
53
<blockquote><h3>Изучите Python на Хекслете</h3>
54
<p>Пройдите нашу профессию "<a>Python-разработчик</a>", чтобы поменять свою жизнь и стать бэкенд-программистом.</p>
54
<p>Пройдите нашу профессию "<a>Python-разработчик</a>", чтобы поменять свою жизнь и стать бэкенд-программистом.</p>
55
</blockquote>
55
</blockquote>