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