FastAPI + SQLModel 实战:标准项目结构下,一个模型搞定数据库与 API

简介: SQLModel 实现“一模型双用”:单个类同时作为数据库表与 Pydantic API 模型,天然支持字段校验、类型提示、OpenAPI 文档生成,彻底消除重复定义,提升开发效率与一致性。(239字)

零重复代码!用 SQLModel 实现 数据库模型Pydantic 请求/响应模型 的统一,同时保留字段校验、类型提示与文档自动生成。

image.png


🎯 为什么 SQLModel 能“一个模型走天下”?

在传统 FastAPI 项目中,你通常要写:

  • UserCreate(请求体)
  • UserRead(响应体)
  • UserUpdate(部分更新)
  • UserDB(SQLAlchemy ORM 模型)

而 SQLModel 的核心优势是:

继承自 Pydantic 的 BaseModel → 支持字段校验、类型提示、OpenAPI 文档
同时是 SQLAlchemy 的 ORM 模型 → 可直接用于数据库操作
一套代码,双重身份 → 无需重复定义!


📁 标准 FastAPI 项目结构

fastapi-sqlmodel-user/
├── main.py
├── database.py
├── models/
│   └── user.py
├── api/
│   └── v1/
│       └── users.py
└── requirements.txt

1️⃣ 定义 唯一 的 User 模型(models/user.py

# models/user.py
from typing import Optional
from sqlmodel import SQLModel, Field

class User(SQLModel, table=True):
    """
    这个类既是:
    - 数据库表(通过 table=True)
    - Pydantic 模型(用于 API 输入/输出)
    - 自动支持字段校验(如必填、类型、约束)
    """
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str = Field(min_length=1, max_length=100)  # Pydantic 校验生效!
    email: str = Field(unique=True, regex=r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
    age: Optional[int] = Field(default=None, ge=0, le=150)  # 年龄范围校验

🔥 关键点

  • Field() 来自 Pydantic,所以 min_length, regex, ge 等校验全部生效
  • unique=TrueSQLAlchemy 的数据库约束
  • 同一个 Field 同时服务 API 层DB 层

2️⃣ 数据库配置(database.py

# database.py
from sqlmodel import create_engine, Session
from sqlmodel import SQLModel
from models.user import User  # 确保模型被导入

# 使用 SQLite 文件数据库(持久化)
engine = create_engine("sqlite:///./app.db", echo=True)

def create_db_and_tables():
    SQLModel.metadata.create_all(engine)

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

3️⃣ API 路由(api/v1/users.py

# api/v1/users.py
from fastapi import APIRouter, Depends, HTTPException
from sqlmodel import Session, select
from models.user import User
from database import get_session

router = APIRouter(prefix="/v1/users", tags=["users"])

@router.post("/", response_model=User)
def create_user(user: User, session: Session = Depends(get_session)):
    """
    请求体自动用 User 模型校验!
    - name 必填且 1~100 字符
    - email 必须是合法邮箱
    - age 必须在 0~150 之间
    """
    # 检查邮箱是否已存在(unique 约束在 DB 层,但提前校验更友好)
    existing_user = session.exec(select(User).where(User.email == user.email)).first()
    if existing_user:
        raise HTTPException(status_code=400, detail="Email already registered")

    session.add(user)
    session.commit()
    session.refresh(user)  # 获取数据库生成的 id
    return user


@router.get("/", response_model=list[User])
def read_users(session: Session = Depends(get_session)):
    return session.exec(select(User)).all()


@router.get("/{user_id}", response_model=User)
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

4️⃣ 主应用入口(main.py

# main.py
from fastapi import FastAPI
from database import create_db_and_tables, engine
from api.v1.users import router as users_router

app = FastAPI(title="FastAPI + SQLModel 用户管理")

# 初始化数据库
@app.on_event("startup")
def on_startup():
    create_db_and_tables()

# 注册路由
app.include_router(users_router)

5️⃣ 运行 & 测试

pip install fastapi sqlmodel uvicorn[standard]
uvicorn main:app --reload

访问 http://localhost:8000/docs

✅ 测试字段校验

1. 邮箱格式错误(自动拒绝)

POST /v1/users/
{
   
  "name": "张三",
  "email": "invalid-email",
  "age": 30
}

→ 返回 422 错误:"msg": "string does not match regex ..."

2. 年龄超范围

{
   
  "name": "李四",
  "email": "li@example.com",
  "age": 200
}

→ 返回 422:"msg": "ensure this value is less than or equal to 150"

3. 成功创建

{
   
  "name": "王五",
  "email": "wang@example.com",
  "age": 25
}

→ 返回带 id 的完整用户对象


💡 为什么这比传统方式好?

传统方式 SQLModel 方式
需定义 UserCreate, UserRead, UserDB 等多个类 只需一个 User
字段变更需同步多处 一处修改,处处生效
Pydantic 校验和 DB 约束分离 统一在 Field() 中声明
容易遗漏校验或类型不一致 强类型 + IDE 自动补全


相关文章
|
22天前
|
Rust 安全 JavaScript
告别 `print()`!用 VS Code 调试器高效定位 Bug
本文手把手教你用VS Code调试器替代低效`print`:5步定位“越打折越贵”Bug,零代码侵入、实时查变量、支持条件断点与表达式监视。免费、高效、安全——调试本该如此简单!
|
22天前
|
人工智能 运维 安全
2026年OpenClaw(Clawdbot)极速部署与OpenClaw Skills生态运维指南
2026年,开源AI智能体技术进入爆发期,OpenClaw(原Clawdbot、Moltbot)凭借“本地优先、全链路可执行、技能生态丰富”的核心特性,成为个人与轻量团队实现自动化办公的首选工具。它彻底打破了传统AI“只会对话不会执行”的局限,通过标准化的Skills(技能)体系,能够像人类一样调用工具、处理文件、对接系统,完成从内容总结到跨平台推送的全流程任务。
251 10
|
4天前
|
人工智能 安全 Serverless
让 AI Agent 安全“跑”在云端:基于函数计算打造 Agent 代码沙箱
Agent 代码沙箱是保障 AI 智能体安全执行的核心基础设施。依托函数计算构建强隔离、有状态、低成本的 AI 运行时。
|
11天前
|
人工智能 弹性计算 自然语言处理
零门槛上手OpenClaw!阿里云极简部署,三步解锁专属超级AI助理!
OpenClaw是可私有部署的AI数字员工框架,支持通义千问、GPT等多模型,能写代码、查资料、管邮件、自动化办公。阿里云提供一键部署方案:买服务器→开通百炼API→图形化配置,三步搞定,安全高效!
226 12
|
19天前
|
人工智能 安全 Linux
2026年OpenClaw(Clawdbot) Linux部署:本地搭建+ZeroNews访问+云上部署教程
2026年初,OpenClaw(前身为Clawdbot)凭借“私有化运行+全权限实操”的创新定位,迅速成为AI领域的现象级开源项目。这款被称为“真正能做实事的AI”的工具,打破了传统聊天机器人的功能局限,可在本地设备或服务器上独立运行,通过WhatsApp、Telegram等常用聊天软件接收指令,完成文件读写、邮件管理、系统运维等实操任务,所有数据本地存储,隐私安全性拉满。
2956 9
|
23天前
|
数据可视化 Python
MEaSUREs 格陵兰岛月度 MODIS 图像镶嵌图 V001
NASA MEaSUREs格陵兰月度MODIS镶嵌图(V001),提供高分辨率海岸线与冰盖边缘动态监测数据,支持气候变化研究。含Python示例代码,便于快速检索、可视化与下载。(239字)
98 18
|
22天前
|
弹性计算
阿里云无影云电脑计算套餐是什么?120小时/月够用吗?
阿里云无影云电脑计算套餐是按月预付的时长包,含120/250/360小时/月及不限时长选项(小时当月有效、不结转)。超时后按小时后付费,费用封顶至同规格不限时套餐月价,并支持自动关机策略防超支。
220 107
|
22天前
|
Oracle Java 关系型数据库
JDK 21安装教程 Windows版详细步骤+环境变量验证(含java/javac/java -version检测)
JDK(Java SE Development Kit)是Oracle官方提供的Java标准版开发工具包,包含编译器(javac)、运行环境(JRE)及核心类库等,用于Java程序的开发、编译、调试与运行。本文详解JDK 21在Windows下的下载、安装与验证步骤,助力新手快速搭建开发环境。(239字)
1424 114
|
21天前
|
缓存 安全 算法
JAVA面试题速记-java基础
本文系统梳理Java核心知识点:涵盖8种基本数据类型、String/StringBuffer/Builder区别、final/static作用、==与equals差异、Collection接口与Collections工具类对比;详解List/Set/Map集合特性及线程安全方案;解析反射、异常处理(throw/throws)、线程生命周期、同步机制(synchronized/ReentrantLock)、ThreadLocal原理、序列化等关键概念。(239字)
279 134