跳转至

SQLModel 数据库 ORM

为什么要学 SQLModel

SQLModel 是由 FastAPI 作者创建的 Python ORM 库,它将 Pydantic 和 SQLAlchemy 的优势合二为一。一个模型类同时充当数据库表定义、Pydantic 验证模型和 API 序列化模型。这消除了传统项目中需要维护 ORM 模型、Schema 模型和 API 模型三套代码的问题。对于 FastAPI 项目来说,SQLModel 是最自然的数据库方案。


核心概念

概念白话解释用途
SQLModel模型基类同时是 Pydantic Model 和 SQLAlchemy Model
Table表标记table=True 表示映射到数据库表
Field字段定义列属性和验证规则
Session会话数据库操作的上下文
Relationship关系表之间的关联
Engine引擎数据库连接

安装配置

pip install sqlmodel

# 常用数据库驱动
pip install psycopg2-binary  # PostgreSQL
pip install aiosqlite        # SQLite 异步
pip install asyncpg           # PostgreSQL 异步

快速上手

定义模型

from sqlmodel import SQLModel, Field, create_engine, Session, select
from typing import Optional

class User(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str = Field(index=True)
    email: str = Field(unique=True)
    age: Optional[int] = Field(default=None, ge=0, le=150)
    is_active: bool = Field(default=True)

# 创建引擎和表
engine = create_engine("sqlite:///database.db", echo=True)
SQLModel.metadata.create_all(engine)

CRUD 操作

# 创建
with Session(engine) as session:
    user = User(name="Alice", email="alice@example.com", age=30)
    session.add(user)
    session.commit()
    session.refresh(user)
    print(user.id)  # 自动生成的 ID

# 查询
with Session(engine) as session:
    # 单条查询
    user = session.get(User, 1)

    # 条件查询
    statement = select(User).where(User.age >= 25)
    users = session.exec(statement).all()

    # 搜索
    statement = select(User).where(User.name.contains("Ali"))
    users = session.exec(statement).all()

# 更新
with Session(engine) as session:
    user = session.get(User, 1)
    user.age = 31
    session.add(user)
    session.commit()

# 删除
with Session(engine) as session:
    user = session.get(User, 1)
    session.delete(user)
    session.commit()

关系

from sqlmodel import Relationship

class Team(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    members: list["User"] = Relationship(back_populates="team")

class User(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    team_id: Optional[int] = Field(default=None, foreign_key="team.id")
    team: Optional[Team] = Relationship(back_populates="members")

进阶用法

读写分离模型

# 基础模型(用于共享字段定义)
class UserBase(SQLModel):
    name: str
    email: str
    age: Optional[int] = None

# 数据库模型
class User(UserBase, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    hashed_password: str

# 创建请求模型
class UserCreate(UserBase):
    password: str

# 响应模型(不包含密码)
class UserRead(UserBase):
    id: int

# 更新模型
class UserUpdate(SQLModel):
    name: Optional[str] = None
    email: Optional[str] = None
    age: Optional[int] = None

FastAPI 集成

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

app = FastAPI()

def get_session():
    with Session(engine) as session:
        yield session

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

@app.get("/users/", response_model=list[UserRead])
def read_users(skip: int = 0, limit: int = 100, session: Session = Depends(get_session)):
    users = session.exec(select(User).offset(skip).limit(limit)).all()
    return users

@app.get("/users/{user_id}", response_model=UserRead)
def read_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

异步支持

from sqlmodel.ext.asyncio.session import AsyncSession
from sqlalchemy.ext.asyncio import create_async_engine

async_engine = create_async_engine("sqlite+aiosqlite:///database.db")

async def get_async_session():
    async with AsyncSession(async_engine) as session:
        yield session

@app.get("/users/")
async def read_users(session: AsyncSession = Depends(get_async_session)):
    result = await session.exec(select(User))
    return result.all()

复杂查询

from sqlmodel import select, col, or_, and_

# 排序和分页
statement = (
    select(User)
    .where(User.is_active == True)
    .order_by(User.name)
    .offset(0)
    .limit(10)
)

# OR 条件
statement = select(User).where(
    or_(User.age > 30, User.name == "Admin")
)

# JOIN
statement = (
    select(User, Team)
    .join(Team)
    .where(Team.name == "Engineering")
)
results = session.exec(statement).all()

常见问题

Q: 与 SQLAlchemy 的关系?

SQLModel 基于 SQLAlchemy 2.0,完全兼容。可以在同一项目中混用。

Q: 如何处理数据库迁移?

使用 Alembic:

pip install alembic
alembic init migrations
# 配置 env.py 使用 SQLModel.metadata
alembic revision --autogenerate -m "initial"
alembic upgrade head

Q: 性能如何?

与 SQLAlchemy 相同。Pydantic v2 带来的性能提升也适用于 SQLModel。


参考资源

  • 官网:https://sqlmodel.tiangolo.com/
  • GitHub:https://github.com/tiangolo/sqlmodel
  • 教程:https://sqlmodel.tiangolo.com/tutorial/
  • FastAPI 集成:https://sqlmodel.tiangolo.com/tutorial/fastapi/