【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 防止"对象失效"问题
相关推荐
肆仲冬35 分钟前
不用框架,用 TypeScript 从零搭一个 Agent 框架
前端
林语琛43 分钟前
我写的 switch…break 被 Babel 偷偷吞了
前端·javascript·babel
烈风逍遥1 小时前
第六篇:RAG 知识库构建与检索全链路
前端·人工智能·后端
爱丶不疚1 小时前
什么是 Jev 决策模型?它适合干什么?
前端·agent
江畔柳前堤1 小时前
字节跳动·大模型应用知识手册
前端·人工智能·深度学习·opencv·目标检测·重构·transformer
LEE2 小时前
前端转型全栈 03:接口失败也返回 200,OpenAPI 契约与错误码怎么定
前端·后端·全栈
我命由我123452 小时前
CSS - CSS 媒体查询 orientation
前端·javascript·css·html·css3·html5·js
lichenyang4533 小时前
让 VK 小程序调用 HarmonyOS 原生能力:一次跨端 Bridge SDK 的设计与实践
前端
阳光宅男@李光熠3 小时前
【电子通识】一起学习TDK的EMC基础——电池兼容设计方法概述
java·前端·数据库
南雨北斗4 小时前
vue3项目中的env.d.ts文件
前端