【 FastAPI 】ORM

**FastAPI ORM **

📚 一、ORM 是什么?

对象关系映射(Object-Relational Mapping, ORM)

是一种编程技术,用于在 面向对象语言 (如 Python)与 关系型数据库 (如 MySQL)之间建立映射桥梁。

"它允许开发者通过操作对象的方式与数据库进行交互,并且无需编写复杂的 SQL 语句"
举例:

  • 不用写 SELECT * FROM book WHERE name = 'Python'
  • 可以直接写:db.query(Book).filter(Book.name == 'Python').first()

二、ORM 使用五大步骤

步骤 内容
1. 导入模块 标准库导入
2. 创建引擎 异步 + 池配置
3. 定义模型 类结构清晰,继承统一
4. 启动建表 自动建表,无需手动
5. 操作数据 增删改查全功能实现
复制代码
---
## 步骤 1:导入 ORM 模块
```python
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from sqlalchemy import DateTime, func, String, Float, select
from sqlalchemy.sql.functions import user

说明:

  • create_async_engine:创建异步数据库引擎
  • async_sessionmaker:会话工厂
  • AsyncSession:异步会话对象
  • DeclarativeBase:ORM 基类
  • Mapped / mapped_column:定义模型字段
  • select():SQL 查询构造器
  • func.now():数据库函数,自动获取当前时间

步骤 2:创建异步数据库引擎

python 复制代码
# 数据库连接 URL 格式:   驱动:          //用户名: 密码 @主机:     端口  /数据库名?字符集
ASYNC_DATABASE_URL = "mysql+aiomysql://root:123456@localhost:3306/FastAPI_first?charset=utf8mb4"
# 创建异步数据库引擎(管理数据库连接池)
async_engine = create_async_engine(
    ASYNC_DATABASE_URL,
    echo=True,             # 调试用:打印所有 SQL 语句
    pool_size=20,          # 连接池大小
    max_overflow=10,       # 超出后最多额外创建 10 个连接
    pool_recycle=3600      # 连接闲置超 1 小时自动回收
)

关键点

  • mysql+aiomysql:使用异步驱动
  • charset=utf8mb4:支持 emoji 与多语言
  • echo=True:仅限开发环境,生产建议设为 False
    警告pool_size=20 过大,生产环境建议设为 10

步骤 3:定义模型类

基类Base:统一时间字段

python 复制代码
class Base(DeclarativeBase):
    """ORM 基类,所有模型类都继承自此类"""
    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="更新时间"
    )

书籍模型类 → 对应 book

python 复制代码
class Book(Base):
    """书籍模型,对应数据库中的 book 表"""
    __tablename__ = "book"
    # mapped_column()  定义数据库列的属性
    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="出版社")

用户模型类 → 对应 user

python 复制代码
class User(Base):
    """用户模型,对应数据库中的 user 表"""
    __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 的 create_time / update_time

所有模型继承 Base,自动获得 create_time 和 update_time。


步骤 4:启动时创建表

目标:应用启动时自动根据模型类生成数据库表结构。

python 复制代码
async def create_table():
    """在应用启动时自动创建数据库表"""
    async with async_engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all) 
        # run_sync: 运行同步方法
        # Base.metadata.create_all: 根据模型类自动创建所有表
# 注册启动事件:应用启动时自动执行 create_table()
@app.on_event("startup")
async def startup_event():
    """FastAPI 应用启动时触发的事件"""
    print("正在初始化数据库...")
    await create_table()
    print("数据库初始化完成!")

startup 事件:应用启动自动建表

使用了 async_engine.begin() + run_sync() 实现异步建表
最佳实践 :避免手动运行 init_db.py


步骤 5:操作数据

TODO: 在这里添加 CRUD 操作的路由

1、创建会话工厂

2、编写依赖项获取数据库会话

3、实现增删改查接口

1. 创建会话工厂

python 复制代码
Async_sessionLocal = async_sessionmaker(
    bind=async_engine,                #绑定数据库引擎
    class_=AsyncSession,              #指定会话类
    expire_on_commit=False            #  关键!必须设置 #提交事务后,session 指定会话不过期 不会重新查询数据库
)

** expire_on_commit=False 是核心灵魂!**

如果不设,提交事务后对象失效 → 后续无法访问属性!


2. 编写依赖项 get_db()

python 复制代码
async def get_db():
    """获取数据库会话"""
    async with Async_sessionLocal() as session:
        try:
            yield session
        except Exception:
            await session.rollback()
            raise
        finally:
            await session.close()

使用了 yield + async with 实现:

  • 自动创建会话
  • 自动关闭
  • 异常时自动回滚
  • 通过 Depends 传递给路由函数

3. 实现 CRUD 路由

路由 功能 使用方法
/book/books 获取所有书籍 await db.execute(select(Book))
/book/book 获取第一本 .limit(1).first()
/book/book/{id} 按 ID 查询 where(Book.id == id)
/book/book_by_name 按名称查询 where(Book.name == name)
/book/search_book 搜索价格 ≥ 200 where(Book.price >= 200)
/user/users 获取所有用户 select(User)

所有操作使用 await db.execute(select(...)),异步安全!


python 复制代码
@app.get("/book/books")
async def get_book_list(db: AsyncSession = Depends(get_db)):  #Depends(get_db) 告诉 FastAPI:"这个参数依赖于 get_db() 函数的返回值"FastAPI 会自动调用 get_db(),并将结果注入到 db 参数
    """获取所有书籍列表"""
    # 查询操作 await db.excute(select(模型类)) 返回一个 ORM 对象
    # 获取所有数据 scalars(),all()
    # 获取指定数据
    # 1、scalars().first()
    # 2、get(模型类,主键值)

    result = await db.execute(select(Book)) # 等待这个异步操作执行完毕,然后获取结果
    book = result.scalars().all()         # .scalars() - 从 Row 对象中提取出 Book 模型对象.all() - 将所有 Book 对象收集到一个列表中
    # 将 SQLAlchemy 对象转换为字典列表
    return book
# 获取第一个书籍
python 复制代码
@app.get("/book/book")
async def get_book(db: AsyncSession = Depends(get_db)):
    """获取第一个书籍"""
    result = await db.execute(select(Book).limit(1))
    book = result.scalars().first()
    return book

@app.get("/book/book/{id}")
async def get_book_by_id(id: int, db: AsyncSession = Depends(get_db)):
    """获取指定 ID 的书籍"""
    result = await db.execute(select(Book).where(Book.id == id))
    book = result.scalars().first()
    return book
@app.get("/book/book_by_name")
async def get_book_by_name(name: str, db: AsyncSession = Depends(get_db)):
    """获取指定名称的书籍"""
    result = await db.execute(select(Book).where(Book.name == name))
    book = result.scalars().first()
    return book
@app.get("/book/search_book")
async def search_book(name: str, db: AsyncSession = Depends(get_db)):
    """搜索书籍"""
    result = await db.execute(select(Book).where(Book.price>=200))
    book = result.scalars().all()
    return book

@app.get("/user/users")
async  def get_user_list(db:AsyncSession = Depends(get_db)):
    """获取所有用户列表"""
    result = await db.execute(select(User))
    user = result.scalars().all()
    return user

三、重要的知识点

你写的 补充说明
scalars().all() Row 对象中提取出 Book 实例(不是 Row
.scalars().first() 获取第一个对象,返回 Book 实例
where(...) 条件查询,避免 SQL 注入
func.now() 数据库时间函数,自动获取当前时间
expire_on_commit=False 核心!必须设置,否则对象失效

警告

如果 expire_on_commit=True,你执行了 await db.commit() 后,再尝试 .name 会报错:
AttributeError: 'Book' object has no attribute 'name'

相关推荐
曲幽2 天前
FastAPI + SQLAlchemy 2.0 通用CRUD操作手册 —— 从同步到异步,一次讲透
python·fastapi·web·async·sqlalchemy·session·crud·sync·with
wytraining2 天前
快速入门 FastAPI 项目
jvm·oracle·fastapi
码界筑梦坊2 天前
94-基于Python的商品物流数据可视化分析系统
开发语言·python·mysql·信息可视化·数据分析·毕业设计·fastapi
zhz52142 天前
一个简单、轻量级且安全的离线GIS 系统架构设计
安全·系统架构·vue·gis·fastapi
L-影3 天前
FastAPI全解析(下):除了快,它还能干多少脏活累活?
python·fastapi
码界筑梦坊3 天前
302-基于Python的安卓应用市场数据可视化分析推荐系统
开发语言·python·信息可视化·毕业设计·fastapi
Ares-Wang3 天前
flask、flask-restful、fastAPI
flask·restful·fastapi
fTiN CAPA3 天前
开源模型应用落地-FastAPI-助力模型交互-进阶篇-中间件(四)
开源·交互·fastapi
深藏功yu名4 天前
Docker+FastAPI+千问API,复刻豆包式流式聊天界面
docker·容器·fastapi
费弗里4 天前
新版本Dash完美支持原生FastAPI后端
python·fastapi·dash