【FastAPI】 + SQLAlchemy 异步 ORM 实现完整 CRUD 操作

从零实战:FastAPI + SQLAlchemy 异步 ORM 实现完整 CRUD 操作(附完整代码)

一、为什么要学「FastAPI + SQLAlchemy 异步 ORM」?

在现代 Web 服务中,数据库是核心组件。然而,传统同步操作(如 MySQLdb)在高并发场景下容易成为瓶颈。

FastAPI + SQLAlchemy 异步 ORM 的组合,具备以下优势:

  • ✅ 高性能(异步非阻塞 I/O)
  • ✅ 代码简洁(面向对象建模)
  • ✅ 安全可靠(SQL 注入防护)
  • ✅ 自动生成 API 文档(Swagger UI)
  • ✅ 与 Pydantic 完美结合,实现数据验证

本文将基于 你今天学习的完整内容 ,带你从零实现一个完整的 图书管理系统 的 CRUD 功能!


二、环境准备(开发前置)

bash 复制代码
pip install fastapi uvicorn sqlalchemy sqlalchemy-asyncio aiomysql pydantic

✅ 推荐使用 uvicorn 启动服务:

bash 复制代码
uvicorn main:app --reload

三、核心步骤一:定义模型类(ORM 映射)

python 复制代码
from sqlalchemy import DateTime, func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from datetime import datetime

class Base(DeclarativeBase):
    # 自动生成创建与更新时间
    create_time: Mapped[datetime] = mapped_column(
        DateTime,
        insert_default=func.now(),
        default=func.now(),
        comment="创建时间"
    )
    update_time: Mapped[datetime] = mapped_column(
        DateTime,
        insert_default=func.now(),
        default=func.now(),
        onupdate=func.now(),
        comment="更新时间"
    )

class Book(Base):
    __tablename__ = "book"

    id: Mapped[int] = mapped_column(primary_key=True, comment="ID")
    name: Mapped[str] = mapped_column(String(255), comment="书名")
    author: Mapped[str] = mapped_column(String(255), comment="作者")
    price: Mapped[float] = mapped_column(Float, comment="价格")
    publisher: Mapped[str] = mapped_column(String(255), comment="出版社")

class User(Base):
    __tablename__ = "user"

    id: Mapped[int] = mapped_column(primary_key=True, comment="ID")
    username: Mapped[str] = mapped_column(String(255), comment="用户名")
    password: Mapped[str] = mapped_column(String(255), comment="密码")
    email: Mapped[str] = mapped_column(String(255), comment="邮箱")

✅ ✅ 小技巧:所有模型继承 Base,自动拥有时间字段!


四、核心步骤二:创建数据库引擎 & 会话工厂

python 复制代码
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker

ASYNC_DATABASE_URL = "mysql+aiomysql://root:123456@localhost:3306/FastAPI_first?charset=utf8mb4"

async_engine = create_async_engine(
    ASYNC_DATABASE_URL,
    echo=True,
    pool_size=20,
    max_overflow=10,
    pool_recycle=3600
)

# 创建会话工厂
Async_sessionLocal = async_sessionmaker(
    bind=async_engine,
    class_=AsyncSession,
    expire_on_commit=False  # 关键!避免提交后对象失效!
)

五、核心步骤三:依赖注入 ------ 获取数据库会话

python 复制代码
from fastapi import Depends, HTTPException

async def get_db():
    async with Async_sessionLocal() as session:
        try:
            yield session
        except Exception:
            await session.rollback()
            raise
        finally:
            await session.close()

从此,每个路由函数都可以通过 db: AsyncSession = Depends(get_db) 自动获取数据库连接!


六、核心步骤四:CRUD 完整路由实现(精华!)

1. 查询所有书籍(GET /book/books)

python 复制代码
@app.get("/book/books")
async def get_book_list(db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(Book))
    books = result.scalars().all()
    return books

2. 查询单个书籍(按 ID)

python 复制代码
@app.get("/book/book/{id}")
async def get_book_by_id(id: int, db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(Book).where(Book.id == id))
    book = result.scalars().first()
    if not book:
        raise HTTPException(status_code=404, detail="书籍不存在")
    return book

3. 模糊查询(LIKE)

python 复制代码
@app.get("/book/search_book_like")
async def search_book_like(name: str, db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(Book).where(Book.name.like(f"%{name}%")))
    books = result.scalars().all()
    return books

4. 聚合查询(MAX、SUM、AVG)

python 复制代码
@app.get("/book/count")
async def get_book_count(db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(func.sum(Book.price)))
    total_price = result.scalar()
    return {"total_price": total_price}

5. 分页查询(OFFSET + LIMIT)

python 复制代码
@app.get("/book/page")
async def get_page(
    page: int = 1,
    page_size: int = 3,
    db: AsyncSession = Depends(get_db)
):
    result = await db.execute(
        select(Book)
        .offset((page - 1) * page_size)
        .limit(page_size)
    )
    books = result.scalars().all()
    return books

6. 增加书籍(POST /book/add_book)

python 复制代码
from pydantic import BaseModel

class BookCreate(BaseModel):
    name: str
    author: str
    price: float
    publisher: str

@app.post("/book/add_book")
async def add_book(book: BookCreate, db: AsyncSession = Depends(get_db)):
    book_obj = Book(**book.dict())
    db.add(book_obj)
    await db.commit()
    await db.refresh(book_obj)
    return book_obj

自动校验类型,无须手动判断数据合法性


7. 更新书籍(PUT /book/update_book/{id})

python 复制代码
class BookUpdate(BaseModel):
    name: str
    author: str
    price: float
    publisher: str

@app.put("/book/update_book/{id}")
async def update_book(id: int, book: BookUpdate, db: AsyncSession = Depends(get_db)):
    book_obj = await db.get(Book, id)
    if not book_obj:
        raise HTTPException(status_code=404, detail="书籍不存在")
    
    book_obj.name = book.name
    book_obj.author = book.author
    book_obj.price = book.price
    book_obj.publisher = book.publisher

    await db.commit()
    return book_obj

8. 删除书籍(DELETE /book/delete_book/{id})

python 复制代码
@app.delete("/book/delete_book/{id}")
async def delete_book(id: int, db: AsyncSession = Depends(get_db)):
    book_obj = await db.get(Book, id)
    if not book_obj:
        raise HTTPException(status_code=404, detail="书籍不存在")
    
    await db.delete(book_obj)
    await db.commit()
    return {"message": "删除成功"}

七、核心亮点总结

技能 说明
Depends(get_db) 自动注入数据库会话,避免重复代码
select().where() SQL 查询构建器,比原生 SQL 更安全
scalars().all() 提取 ORM 对象列表
await db.commit() 提交事务
Pydantic Model 请求数据自动校验
HTTPException 统一错误处理
expire_on_commit=False 防止"对象失效"问题
相关推荐
贾铭9 小时前
如何实现一个网页版的剪映(五)如何跳转到视频某一帧
前端·后端
林恒smileZAZ9 小时前
CSS 滚动驱动动画(scroll-timeline):无 JS 实现滚动特效
前端·javascript·css
俺不会敲代码啊啊啊9 小时前
el-table实现行拖拽(包含展开项)
前端·vue.js·typescript
LIO9 小时前
React Router 极简指南(v6+)
前端·react.js
明月_清风9 小时前
从 AST 视角看透前端工程化:一条编译管线如何串联起所有工具
前端
架构源启9 小时前
2026 进阶篇:Spring Boot响应式编程 + Spring AI 1.1.4 流式实战 + Vue前端完整实现(避坑指南)
java·前端·vue.js·人工智能·spring boot·spring·ai编程
白开水都有人用9 小时前
前端 AES 加密 + 后端解密 + MD5 校验登录
前端
OpenTiny社区9 小时前
还在手写 AI 聊天页?这款 Vue3 气泡组件,直接搞定流式对话!
前端·vue.js·ai编程
毛骗导演9 小时前
Cladue Code 源码解析-键盘事件与 Vim 模式:parse-keypress 解析状态机
前端·架构
渐儿9 小时前
GLB 模型压缩 — 完整流程与代码映射
前端