HTML Diff
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>