【 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'

相关推荐
Maiko Star9 小时前
从零落地「AI掘金头条」新闻模块:FastAPI + SQLAlchemy 异步实战
python·fastapi
喜欢的名字被抢了11 小时前
FastAPI 接入异步 PostgreSQL 完成任务 CRUD 与数据库迁移
数据库·postgresql·fastapi
Esaka_Forever13 小时前
uvicorn main.py 命令说明
fastapi
巴啦啦小魔仙变身2 天前
FastAPI 01 Create Demo
python·fastapi
李昊哲小课3 天前
fastapi sse websocket 奶茶店实时订单看板
人工智能·python·websocket·网络协议·fastapi·sse
李昊哲小课3 天前
fastapi sse websocket 智能家居实时控制台
python·websocket·智能家居·fastapi·sse
赵广陆3 天前
SQLAlchemy ORM 超全教程
python·fastapi
为啥全要学4 天前
fastapi websocket全双工通信
websocket·网络协议·fastapi
这就是佬们吗4 天前
企业Agent落地为什么需要RAG?
人工智能·python·fastapi
鲨鱼辣钊4 天前
【FastAPI 筑基 - Day2】彩色日志封装 + Python 装饰器精讲
fastapi