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

相关推荐
别抢我的锅包肉1 天前
【FastAPI】 响应类型详解:从默认 JSON 到自定义响应
python·fastapi
龙腾AI白云2 天前
大模型Prompt实战:精准生成专业技术文档
plotly·pyqt·fastapi·tornado·dash
星星也在雾里3 天前
Dify Agent + FastAPI + PostgreSQL实现数据库查询
数据库·人工智能·fastapi
曲幽3 天前
FastAPI + Celery 实战:异步任务里调用 Redis 和数据库的全解析,及生产级组织方案
redis·python·fastapi·web·async·celery·task·queue
wggmrlee4 天前
Mac安装Anaconda
python·fastapi
skywalk81634 天前
Kotti Next:使用FastAPI+Vue 3构建的现代无头CMS-Kotti CMS的精神继承者(使用WorkBuddy AI自动编程)
前端·vue.js·人工智能·fastapi·kotti
曲幽4 天前
FastAPI里玩转Redis和数据库的正确姿势,别让异步任务把你坑哭了!
redis·python·mysql·fastapi·web·celery·sqlalchemy·task·backgroundtask
Thomas.Sir5 天前
第五章:RAG知识库开发之【利用RAG知识库实现智能AI系统:从零构建企业级智能问答应用】
人工智能·python·vue·状态模式·fastapi·智能
呱牛do it5 天前
企业级软件研发团队绩效考核系统开发(持续更新 Day 8)
python·fastapi·研发管理