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 章。

🌟 欢迎大家来观看!

相关推荐
一个天蝎座 白勺 程序猿2 小时前
从300秒到3秒:我在KES上“干掉“标量子查询的性能优化实践
性能优化·量子计算·kingbasees·向量化执行
Jinkxs2 小时前
Rust 性能优化全流程:从 flamegraph 定位瓶颈到 unsafe 与 SIMD 加速,响应快 2 倍
开发语言·性能优化·rust
醉颜凉2 小时前
Elasticsearch性能优化:JVM GC调优全攻略,彻底解决集群卡顿、吞吐量下降问题
jvm·elasticsearch·性能优化
梵得儿SHI2 小时前
Vue 项目实战与性能优化全攻略:从代码、渲染到首屏,一站式解决卡顿慢加载
前端·vue.js·性能优化·vite·前端面试·前端优化·首屏优化
ShyanZh2 小时前
【skill】HTML PPT Skill:用 Claude Code 一句话生成专业演示文稿
前端·ai·html·powerpoint·skill
BAGAE2 小时前
星链卫星数据获取:从太空安全到实时通信的技术革命
网络·数据结构·数据库·算法·云计算·hbase
zh_xuan2 小时前
Android导出并查看数据库
数据库·sqlite
AI视觉网奇2 小时前
three教学 3d资产拼接源代码
前端·css·css3
小短腿的代码世界2 小时前
Qt定时器高精度架构:从QTimer源码到纳秒级定时调度
数据库·qt·架构