$ sudo teach|
    $ sudo teach|
    IT school
  • Telegram
  • Партнёрам
  • Все курсы
$ sudo teach IT
OOO "SALEPROFIT"Контакты и реквизитыIT-Park Logo

Школа

  • Блог
  • Проверить сертификат

Сотрудничество

  • Стать учителем
  • Партнёрская программа
  • О проекте

Право

  • Оферта
  • Политика конфиденциальности

© 2023–2026 $ sudo teach IT™. All Rights Reserved. Public user contributions licensed under CC BY-SA 4.0 license with attribution required
TelegramGitHubYouTube
ГлавнаяБлогSQLModel vs SQLAlchemy: что выбрать для Python-проекта с FastAPI

SQLModel vs SQLAlchemy: что выбрать для Python-проекта с FastAPI

$ sudo teach IT
·18 июля 2026 г.·8 мин·107
SQLModel vs SQLAlchemy: что выбрать для Python-проекта с FastAPI

Сравниваем два подхода к работе с базами данных в Python-экосистеме: где SQLModel упрощает жизнь, а где мощность SQLAlchemy незаменима. С примерами моделей, запросов и миграций.

Содержание
Что такое SQLAlchemy и почему оно до сих пор актуальноЧто такое SQLModel и какую проблему он решаетПаттерн разделения схем в SQLModelЗапросы: сравниваем подходыПростые выборкиСложные JOIN-запросыМиграции: Alembic в обоих случаяхAsync: где и как работаетКогда выбирать SQLModelКогда выбирать SQLAlchemyРеальный компромисс: SQLModel поверх SQLAlchemyСостояние проекта и рискиВывод

Когда разработчик начинает новый проект на FastAPI, один из первых архитектурных вопросов — как организовать слой работы с базой данных. Исторически ответ был очевиден: SQLAlchemy. Это зрелая, мощная и хорошо задокументированная библиотека, которая де-факто стала стандартом для Python ORM. Но в 2021 году Себастьян Рамирес — автор FastAPI и Pydantic — выпустил SQLModel, позиционируя его как более удобный способ работать с SQL в связке с FastAPI и Pydantic.

С тех пор вопрос «что выбрать» стал регулярно всплывать в сообществах. Ответ, как это обычно бывает в инженерии, зависит от контекста. Давайте разберём оба инструмента честно: без маркетинга, с примерами кода и реальными компромиссами.


Что такое SQLAlchemy и почему оно до сих пор актуально

SQLAlchemy существует с 2006 года. Это не просто ORM — это два слоя в одном пакете: Core (низкоуровневый SQL Expression Language) и ORM (высокоуровневый маппинг объектов). Версия 2.0, вышедшая в 2023 году, принесла нативную поддержку async, улучшенную типизацию и более чистый API.

Ключевые характеристики SQLAlchemy:

  • Зрелость и стабильность: 18+ лет в продакшене, огромная экосистема, поддержка всех мажорных СУБД.
  • Гибкость: можно работать как через ORM, так и напрямую через Core — строить сложные запросы, использовать raw SQL, управлять транзакциями вручную.
  • Alembic: де-факто стандарт для миграций в Python-проектах, разработанный тем же автором.
  • Async-поддержка: через AsyncSession и asyncpg/aiosqlite.

Модель на SQLAlchemy 2.0 с декларативным стилем выглядит так:

code
from sqlalchemy import String, Integer, ForeignKey
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship

class Base(DeclarativeBase):
    pass

class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    name: Mapped[str] = mapped_column(String(100), nullable=False)
    email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
    posts: Mapped[list["Post"]] = relationship(back_populates="author")

class Post(Base):
    __tablename__ = "posts"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    title: Mapped[str] = mapped_column(String(200))
    author_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
    author: Mapped["User"] = relationship(back_populates="posts")

Это уже значительно лучше, чем классический Column-синтаксис из SQLAlchemy 1.x. Типизация через Mapped работает с mypy и IDE. Но здесь нет ничего общего с Pydantic — и это ключевая точка трения при работе с FastAPI.


Что такое SQLModel и какую проблему он решает

SQLModel — это надстройка над SQLAlchemy и Pydantic одновременно. Технически это тонкая обёртка: под капотом SQLModel использует SQLAlchemy для работы с базой данных и Pydantic для валидации данных. Идея в том, чтобы один класс мог служить одновременно ORM-моделью и Pydantic-схемой.

Та же структура на SQLModel:

code
from typing import Optional
from sqlmodel import SQLModel, Field, Relationship

class User(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str = Field(max_length=100)
    email: str = Field(max_length=255, unique=True)
    posts: list["Post"] = Relationship(back_populates="author")

class Post(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    title: str = Field(max_length=200)
    author_id: Optional[int] = Field(default=None, foreign_key="user.id")
    author: Optional[User] = Relationship(back_populates="posts")

Синтаксис заметно компактнее. Но главное преимущество проявляется при интеграции с FastAPI: одну и ту же модель (или её вариации) можно использовать как схему запроса, схему ответа и ORM-модель.

Паттерн разделения схем в SQLModel

На практике рекомендуется разделять модели на несколько классов:

code
from typing import Optional
from sqlmodel import SQLModel, Field

# Базовая схема — общие поля
class UserBase(SQLModel):
    name: str
    email: str

# ORM-модель — добавляем table=True и id
class User(UserBase, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)

# Схема для создания — без id
class UserCreate(UserBase):
    password: str

# Схема для ответа API — без пароля
class UserRead(UserBase):
    id: int

Этот паттерн позволяет переиспользовать логику без дублирования. В FastAPI это выглядит так:

code
from fastapi import FastAPI, Depends
from sqlmodel import Session, select

app = FastAPI()

@app.post("/users/", response_model=UserRead)
def create_user(user: UserCreate, session: Session = Depends(get_session)):
    db_user = User.model_validate(user)
    session.add(db_user)
    session.commit()
    session.refresh(db_user)
    return db_user

@app.get("/users/{user_id}", response_model=UserRead)
def get_user(user_id: int, session: Session = Depends(get_session)):
    user = session.get(User, user_id)
    if not user:
        raise HTTPException(status_code=404, detail="User not found")
    return user

Никакого ручного маппинга между ORM-объектом и Pydantic-схемой. Это реальное сокращение бойлерплейта.


Запросы: сравниваем подходы

Простые выборки

SQLAlchemy 2.0:

code
from sqlalchemy import select
from sqlalchemy.orm import Session

def get_active_users(session: Session) -> list[User]:
    stmt = select(User).where(User.is_active == True).order_by(User.name)
    return session.scalars(stmt).all()

SQLModel:

code
from sqlmodel import Session, select

def get_active_users(session: Session) -> list[User]:
    stmt = select(User).where(User.is_active == True).order_by(User.name)
    return session.exec(stmt).all()

Разница минимальна: session.scalars() vs session.exec(). SQLModel использует собственный метод exec, который возвращает типизированный результат без лишних обёрток.

Сложные JOIN-запросы

Здесь начинаются различия. SQLAlchemy Core даёт полный контроль:

code
from sqlalchemy import select, func, join

stmt = (
    select(
        User.id,
        User.name,
        func.count(Post.id).label("post_count")
    )
    .join(Post, Post.author_id == User.id, isouter=True)
    .group_by(User.id, User.name)
    .having(func.count(Post.id) > 5)
    .order_by(func.count(Post.id).desc())
)

result = session.execute(stmt).all()

SQLModel поддерживает тот же синтаксис (он наследует его от SQLAlchemy), но типизация сложных агрегатных запросов работает хуже — IDE и mypy часто не могут вывести типы для select() с несколькими колонками.


Миграции: Alembic в обоих случаях

Ни SQLModel, ни SQLAlchemy не имеют встроенного инструмента миграций — оба используют Alembic. Разница в настройке.

Для SQLAlchemy env.py выглядит стандартно:

code
from myapp.models import Base
target_metadata = Base.metadata

Для SQLModel нужно импортировать все модели явно, чтобы они зарегистрировались в метаданных:

code
from sqlmodel import SQLModel
# Важно: импортировать все модели до использования metadata
from myapp.models import User, Post  # noqa: F401

target_metadata = SQLModel.metadata

Типичная ошибка при работе с SQLModel и Alembic — забыть импортировать модели. В этом случае autogenerate не увидит таблицы и сгенерирует пустую миграцию. SQLAlchemy в этом плане ведёт себя аналогично, но документации и примеров для него значительно больше.


Async: где и как работает

Обе библиотеки поддерживают асинхронный режим, но SQLModel здесь отстаёт.

Async с SQLAlchemy:

code
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import sessionmaker

engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

async def get_users(session: AsyncSession) -> list[User]:
    result = await session.execute(select(User))
    return result.scalars().all()

SQLModel официально поддерживает async начиная с версии 0.0.14, но документация по этой теме остаётся скудной. На практике используют AsyncSession из SQLAlchemy напрямую:

code
from sqlalchemy.ext.asyncio import AsyncSession
from sqlmodel import select

async def get_users(session: AsyncSession) -> list[User]:
    result = await session.exec(select(User))  # exec не работает с AsyncSession!
    return result.all()

Здесь кроется подводный камень: метод exec() из SQLModel не совместим с AsyncSession из SQLAlchemy. Приходится использовать execute() и вручную вызывать .scalars(). Это нарушает одно из ключевых удобств SQLModel.

Тем, кто хочет детально разобраться с этими нюансами и построить правильную архитектуру с нуля, может пригодиться курс по SQLModel — там разбираются именно такие практические сценарии, включая async и интеграцию с FastAPI.


Когда выбирать SQLModel

SQLModel оправдан в следующих сценариях:

Новый проект с FastAPI без сложной логики БД. Если приложение — это в основном CRUD с несколькими таблицами, SQLModel даёт ощутимый выигрыш в скорости разработки. Меньше кода, меньше мест для ошибок, встроенная валидация.

Команда с опытом в Pydantic, но без глубокого знания SQLAlchemy. Синтаксис SQLModel ближе к Pydantic, порог входа ниже.

Прототипирование и MVP. Быстро поднять API с базой данных — SQLModel справляется отлично.

Монолитные API без сложных аналитических запросов. Если нет оконных функций, рекурсивных CTE и хитрых JOIN-стратегий, SQLModel не будет ограничивать.


Когда выбирать SQLAlchemy

Сложные запросы и аналитика. Оконные функции, CTE, сложные агрегации — SQLAlchemy Core даёт полный контроль над генерируемым SQL.

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

Большие команды с разными уровнями опыта. Документации, туториалов и Stack Overflow-ответов для SQLAlchemy на порядок больше.

Требования к производительности. Тонкая настройка стратегий загрузки (lazy, selectin, joined), управление пулом соединений, оптимизация N+1 — всё это в SQLAlchemy задокументировано и проверено годами.

Async как основной режим работы. Async SQLAlchemy зрелее, лучше задокументирован и не имеет описанных выше несовместимостей.


Реальный компромисс: SQLModel поверх SQLAlchemy

Важно понимать: SQLModel не конкурирует с SQLAlchemy — он строится на его основе. Это означает, что при необходимости можно использовать SQLAlchemy-конструкции напрямую внутри SQLModel-проекта.

code
from sqlmodel import SQLModel, Field, Session
from sqlalchemy import text

class User(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str

# Когда нужен сырой SQL или сложный запрос — используем SQLAlchemy напрямую
def get_users_raw(session: Session):
    result = session.execute(text("SELECT id, name FROM user WHERE is_active = true"))
    return result.fetchall()

Это позволяет начать с SQLModel для простых сущностей и постепенно переходить к SQLAlchemy там, где нужна мощность.


Состояние проекта и риски

Честный разговор о SQLModel невозможен без упоминания его темпа развития. На момент написания статьи проект развивается медленнее, чем ожидало сообщество: issues накапливаются, некоторые баги существуют годами. SQLAlchemy в этом плане значительно надёжнее — за ним стоит коммерческая поддержка (Zope Foundation и спонсоры) и многолетняя история.

Если проект критичен для бизнеса и требует долгосрочной поддержки — это аргумент в пользу SQLAlchemy. Если это внутренний инструмент или стартап, где скорость важнее — SQLModel вполне приемлем.


Вывод

SQLModel и SQLAlchemy решают одну задачу разными средствами. SQLModel выигрывает в скорости разработки и интеграции с FastAPI/Pydantic — особенно для простых и средних по сложности приложений. SQLAlchemy выигрывает в зрелости, гибкости и полноте контроля над слоем данных.

Практическая рекомендация: начинайте с SQLModel, если строите FastAPI-приложение с нуля и не предвидите сложных запросов. Используйте SQLAlchemy, если проект долгосрочный, команда большая или требования к базе данных нетривиальны. И помните, что SQLModel не запрещает использовать SQLAlchemy там, где это нужно — это не выбор «или/или».

Если хочется разобраться с SQLModel системно — от базовых моделей до async и миграций — стоит посмотреть на курс по SQLModel: там эти темы разобраны последовательно и с практическими примерами. Но и официальная документация SQLAlchemy 2.0 сейчас заметно улучшилась и остаётся одним из лучших технических ресурсов в Python-экосистеме.

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

Автор

$ sudo teach IT

SQLModel

Курс по теме

SQLModel

С нуля до уверенной работы с базами данных на Python. Подходит для начинающих и тех, кто уже пишет на Python. Разберитесь с SQLModel раз и навсегда — и перестаньте бояться SQL и таблиц.

Открыть курс

Продолжите обучение

Все курсы
Python – для начинающих!

Python – для начинающих!

С нуля до профессионального уровня. Подходит для всех. Учитесь каждый день и овладейте самым популярным языком программирования.

Перейти к курсу
Solidity DLC - поймай, если сможешь

Solidity DLC - поймай, если сможешь

Аудит смарт-контрактов на реальных взломах. The DAO, Parity, Wormhole, Ronin, Euler и ещё 7 разборов: что пошло не так, сколько потеряли и что бы спасло. Атаки и защиты на Foundry, план входа в профессию аудитора.

Перейти к курсу
Ботостроение Telegram

Ботостроение Telegram

Лёгкий, быстрый и доступный способ познакомиться с миром ботостроения в Telegram. Видео, конспекты, практика и помощь – всё у нас на курсе.

Перейти к курсу
Solidity pro - всё или ничего

Solidity pro - всё или ничего

Как пишут контракты, которые работают в основной сети. Наследование и OpenZeppelin, паттерны продакшена, оптимизация газа, ABI, delegatecall и подписи. 7 модулей, 33 урока, а в финале собственное dApp: мини-DAO с токеном, голосованием и казной.

Перейти к курсу

Другие статьи

Какой первый проект выбрать новичку, чтобы не бросить обучение
первый

Какой первый проект выбрать новичку, чтобы не бросить обучение

Подберём 5–7 идей под уровень “с нуля”, объясним, что делать по шагам и как довести проект до результата без перегруза. В конце — как оформить мини-портфолио и что показать, даже если проект маленький.

23 сентября 2026 г.
1820
Python для аналитики на практике: как строить признаки и проверять их воспроизводимость
python

Python для аналитики на практике: как строить признаки и проверять их воспроизводимость

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

17 сентября 2026 г.
1790
Как выучить программирование самому и бесплатно: с чего начать и в каком порядке
выучить

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

Разберём понятный маршрут для новичка: как выбрать первый язык, что учить в первую очередь, какие мини-проекты делать и как не забросить занятия. Дам бесплатные ориентиры и план, чтобы знания складывались в портфолио.

21 сентября 2026 г.
1960
Как начать с HTML и сделать первую страницу: пошагово и без сложных слов
html

Как начать с HTML и сделать первую страницу: пошагово и без сложных слов

Разберём, что нужно новичку, чтобы за 1–2 вечера собрать свою первую веб-страницу: структура HTML, базовые теги, ссылки и картинки, плюс простой чек-лист, как понять, что страница работает правильно.

2 ноября 2026 г.
20
Cargo для продакшена: workspaces, фичи и стратегия версий зависимостей
cargo

Cargo для продакшена: workspaces, фичи и стратегия версий зависимостей

Разберём, как организовать Rust workspace, управлять optional features, фиксировать версии библиотек и уменьшать “дрейф” зависимостей при командной разработке и CI.

18 сентября 2026 г.
1960
Что такое HTML и чем он отличается от CSS — объясню простыми словами и на примерах
такое

Что такое HTML и чем он отличается от CSS — объясню простыми словами и на примерах

Разберём, из чего состоит веб-страница: что делает HTML (смысл и структура) и что делает CSS (оформление). В конце соберёте мини-страницу и поймёте, что нужно учить в каком порядке.

30 октября 2026 г.
130

Комментарии

Станьте первым в обсуждении

Поделитесь мнением о статье или задайте вопрос автору

Содержание

Что такое SQLAlchemy и почему оно до сих пор актуальноЧто такое SQLModel и какую проблему он решаетПаттерн разделения схем в SQLModelЗапросы: сравниваем подходыПростые выборкиСложные JOIN-запросыМиграции: Alembic в обоих случаяхAsync: где и как работаетКогда выбирать SQLModelКогда выбирать SQLAlchemyРеальный компромисс: SQLModel поверх SQLAlchemyСостояние проекта и рискиВывод