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 小时前
RAG--03--Milvus基本用法
数据库·oracle·milvus
支支დ2 小时前
VO by Vercel 前端特定优势:为什么它是构建 AI 应用的新范式
前端·人工智能
TDengine (老段)2 小时前
TDengine 线程模型 — 网络、调度、执行
大数据·数据库·物联网·制造·时序数据库·tdengine·涛思数据
香芋芋圆2 小时前
AI 冲击内卷之下,普通前端如何破局?WebGIS—— 低门槛突围赛道
前端·javascript·人工智能·学习·职场发展
INS_KF3 小时前
【编程笔记】成员函数中两个 const 的区别(const Data &getData() const;)
前端·javascript·笔记
上学的小垃圾3 小时前
01-数据库系统概述
数据库
宿6744 小时前
vue3-async
前端·javascript·vue.js
YXWik64 小时前
记录前端请求接口在浏览器请求响应的Preview和Response展示的一样的问题
前端
二进制漫游记4 小时前
FastAPI项目集成 Qdrant向量数据库+阿里云Embedding完整实战(工具封装+业务调用)
数据库·python·阿里云·embedding
2501_928996224 小时前
GPT-4o换DeepSeek迁移成本多少?中科热备解析API聚合平台技术账本
前端·数据库·人工智能