FastAPI 全栈后端(三):数据库与 ORM

创作者: Yardon | GitHub: github.com/YardonYan | 版本: v1.0 |



ORM 是什么,为什么需要它

ORM(Object-Relational Mapping)像是数据库和 Python 对象之间的翻译官:

复制代码
Python:  user.name = "Yardon"
         ↓ (ORM 翻译)
SQL:     UPDATE users SET name = 'Yardon' WHERE id = 1;

不用写裸 SQL,不需要手动拼接查询字符串,不用处理不同数据库的方言差异。同时 ORM 自动帮你防 SQL 注入。


SQLAlchemy 2.0 快速上手

python 复制代码
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column

DATABASE_URL = "postgresql+asyncpg://user:pass@localhost/dbname"

engine = create_async_engine(DATABASE_URL, echo=False)

class Base(DeclarativeBase):
    pass

async def get_db():
    async with AsyncSession(engine) as session:
        yield session

在 FastAPI 中注入数据库会话

python 复制代码
from fastapi import Depends

@app.get("/users")
async def list_users(db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(User).limit(10))
    return result.scalars().all()

Depends 是 FastAPI 的依赖注入系统。get_db 被调用后返回一个数据库会话,路由函数参数中声明 db: AsyncSession 就能拿到它。


定义模型与关系

python 复制代码
class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
    username: Mapped[str] = mapped_column(String(50), unique=True, index=True)
    email: Mapped[str] = mapped_column(String(120))
    created_at: Mapped[datetime] = mapped_column(server_default=func.now())

    # 一对多:一个用户有多篇文章
    posts: Mapped[list["Post"]] = relationship(back_populates="author")

class Post(Base):
    __tablename__ = "posts"

    id: Mapped[int] = mapped_column(primary_key=True)
    title: Mapped[str] = mapped_column(String(200))
    content: Mapped[str] = mapped_column(Text)
    user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))

    # 反向关系
    author: Mapped["User"] = relationship(back_populates="posts")

异步数据库操作

python 复制代码
# 创建
new_user = User(username="Yardon", email="y@example.com")
db.add(new_user)
await db.commit()

# 查询
result = await db.execute(select(User).where(User.id == 1))
user = result.scalar_one_or_none()

# 分页
result = await db.execute(
    select(User).offset(skip).limit(limit).order_by(User.created_at.desc())
)

# 更新
user.email = "new@example.com"
await db.commit()

# 删除
await db.delete(user)
await db.commit()

数据库迁移:Alembic

数据库表结构会随着项目演进不断变化------加字段、改类型、建新表。Alembic 帮你管理这些变更。

bash 复制代码
pip install alembic
alembic init alembic
bash 复制代码
# 修改模型后:
alembic revision --autogenerate -m "add users table"
alembic upgrade head    # 应用迁移
alembic downgrade -1    # 回滚一步(出问题时救命)

查询优化:N+1 问题

python 复制代码
# ❌ N+1 问题
users = await db.execute(select(User))
for user in users.scalars():
    posts = await db.execute(
        select(Post).where(Post.user_id == user.id)  # 每个用户1次查询!
    )

# ✅ 预加载关系
users = await db.execute(
    select(User).options(selectinload(User.posts))
)
for user in users.scalars():
    print(user.posts)  # 已经在第一步查好了

N+1 问题就像去图书馆借 10 本书,你不是一次拿 10 本,而是拿一本回家、再来拿第二本、再来拿第三本------效率极低。selectinload 就是一次把关联数据全查出来。


连接池与事务管理

python 复制代码
engine = create_async_engine(
    DATABASE_URL,
    pool_size=10,       # 常驻连接数
    max_overflow=20,    # 突发额外连接数
    pool_recycle=3600,  # 连接回收时间(秒)
)

事务管理:

python 复制代码
async def transfer(sender_id, receiver_id, amount, db):
    async with db.begin():  # 自动 commit/rollback
        sender = await db.get(User, sender_id)
        receiver = await db.get(User, receiver_id)
        sender.balance -= amount
        receiver.balance += amount
        # 如果中间出错,两边都不会被修改

本章小结

SQLAlchemy 2.0 + asyncpg 是 FastAPI 的最佳数据库搭档。记住三条:用 selectinload 避免 N+1、用 Alembic 管理迁移、用 async with db.begin() 保证事务一致性。


📌 创作者: Yardon | 🏠 个人网站: GlimmerAI.top

📖 本章是「FastAPI 全栈后端」系列的第 3 章。

🌟 欢迎大家来观看!

相关推荐
用户2930750976692 分钟前
用React 完成Todos 的 组件应用:深入理解单向数据流与组件通信
前端
金海境科技11 分钟前
【服务器数据恢复】H3C CAS虚拟化平台HP存储qcow2文件损坏快照丢失数据恢复案例
服务器·数据库·经验分享
玉宇夕落14 分钟前
React 父子组件通信 Todo List 看透单向数据流与不可变数据
前端
软件开发技术深度爱好者15 分钟前
国际音标魔法实验室工具HTML5实现
前端·html5·英语学习
FogLetter16 分钟前
嘘!WebSocket正在“偷听”你的网络请求——全双工通信的魔法
前端·面试
用户9385156350717 分钟前
从零构建 React Todo 应用:组件化设计与数据流深度解析
前端·javascript
IT_陈寒19 分钟前
Python线程池把我坑惨了,这些盲区你不踩?
前端·人工智能·后端
正儿八经的少年24 分钟前
Spring 事务保证数据一致性
java·数据库·spring
foo1st29 分钟前
MySQL 8.0(Windows)升级笔记
数据库·笔记·mysql
张人玉31 分钟前
基于 Vue 3 + ECharts + Express + SQLite 的可视化大屏与业务管理系统——YOLOv8 高精度车辆行人检测与计数系统
数据库·vue.js·yolo·sqlite·echarts·express