【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 防止"对象失效"问题
相关推荐
持续前行6 分钟前
通过 npm 下载node_modules 某个依赖 ;例如 下载 @rollup/rollup-linux-arm64-gnu
前端·javascript·vue.js
chenyingjian40 分钟前
鸿蒙|能力特性-统一文件预览
前端·harmonyos
毛骗导演41 分钟前
OpenClaw 沙箱执行系统深度解析:一条 exec 命令背后的安全长城
前端·架构
天才聪1 小时前
鸿蒙开发vs前端开发1-父子组件传值
前端
卡尔特斯1 小时前
Android Studio 代理配置指南
android·前端·android studio
李剑一1 小时前
同样做缩略图,为什么别人又快又稳?踩过无数坑后,我总结出前端缩略图实战指南
前端·vue.js
Jolyne_1 小时前
Taro样式重构记录
前端
恋猫de小郭1 小时前
Google 开源大模型 Gemma4 怎么选,本地跑的话需要什么条件?
前端·人工智能·ai编程
文心快码BaiduComate1 小时前
Comate搭载GLM-5.1:长程8H,对齐Opus 4.6
前端·后端·架构
熊猫钓鱼>_>1 小时前
AI驱动的Web应用智能化:WebMCP、WebSkills与WebAgent的融合实践
前端·人工智能·ai·skill·webagent·webmcp·webskills