数据库实战:FastAPI + SQLAlchemy 2.0 + Alembic 从零搭建,踩坑实录
数据库实战FastAPI SQLAlchemy 2.0 Alembic 从零搭建踩坑实录前言在现代 Web 开发中数据库操作是核心环节之一。FastAPI 作为高性能异步框架与 SQLAlchemy 2.0 的组合堪称黄金搭档而 Alembic 则能帮助我们优雅地管理数据库迁移。本文将带你从零搭建一套完整的数据库操作环境并分享我在这条路上踩过的坑。## 环境搭建与依赖配置### 核心依赖版本- Python 3.10± FastAPI 0.104± SQLAlchemy 2.0.23± Alembic 1.13.0± asyncpg (异步 PostgreSQL 驱动)### 踩坑点 1SQLAlchemy 2.0 的异步模式SQLAlchemy 2.0 引入全新的异步 API与旧版本不兼容。新手容易混淆create_engine和create_async_engine。python# database.py - 数据库配置核心文件from sqlalchemy.ext.asyncio import create_async_engine, AsyncSessionfrom sqlalchemy.orm import sessionmaker, DeclarativeBase# 注意必须使用 async 前缀的引擎DATABASE_URL postgresqlasyncpg://user:passwordlocalhost:5432/testdbengine create_async_engine(DATABASE_URL, echoTrue)# AsyncSession 是异步会话工厂async_session sessionmaker( engine, class_AsyncSession, expire_on_commitFalse # 避免事务提交后对象过期)class Base(DeclarativeBase): passasync def get_db() - AsyncSession: 依赖注入获取数据库会话 async with async_session() as session: try: yield session await session.commit() except Exception: await session.rollback() raise finally: await session.close()## 模型定义与数据操作### 踩坑点 2异步操作中的上下文管理忘记在异步函数中使用async with或await会导致连接泄漏。以下是一个完整的用户模型示例python# models.py - 定义数据库模型from sqlalchemy import Column, Integer, String, DateTime, funcfrom sqlalchemy.orm import relationshipfrom database import Baseclass User(Base): __tablename__ users id Column(Integer, primary_keyTrue, indexTrue) username Column(String(50), uniqueTrue, nullableFalse, indexTrue) email Column(String(120), uniqueTrue, nullableFalse) created_at Column(DateTime(timezoneTrue), server_defaultfunc.now()) # 关系定义可选 # posts relationship(Post, back_populatesauthor) def __repr__(self): return fUser(id{self.id}, username{self.username})# schemas.py - Pydantic 模型用于 API 请求验证from pydantic import BaseModel, EmailStrfrom typing import Optionalfrom datetime import datetimeclass UserCreate(BaseModel): username: str email: EmailStrclass UserResponse(BaseModel): id: int username: str email: str created_at: datetime class Config: from_attributes True # 支持 ORM 模式## FastAPI 路由实现### 踩坑点 3异步 CRUD 操作的正确写法很多新手会忘记在数据库操作前加await或者错误地使用同步查询。以下是一个完整的用户管理 APIpython# main.py - FastAPI 应用主文件from fastapi import FastAPI, Depends, HTTPException, statusfrom sqlalchemy.ext.asyncio import AsyncSessionfrom sqlalchemy import selectfrom typing import Listfrom database import get_dbfrom models import Userfrom schemas import UserCreate, UserResponseapp FastAPI(titleUser Management API)app.post(/users/, response_modelUserResponse, status_codestatus.HTTP_201_CREATED)async def create_user(user_data: UserCreate, db: AsyncSession Depends(get_db)): 创建新用户 关键点使用 await 执行异步查询 # 检查用户名是否已存在 result await db.execute( select(User).where(User.username user_data.username) ) existing_user result.scalar_one_or_none() if existing_user: raise HTTPException( status_codestatus.HTTP_400_BAD_REQUEST, detailUsername already exists ) # 创建新用户 new_user User( usernameuser_data.username, emailuser_data.email ) db.add(new_user) await db.flush() # 确保获取到 id await db.refresh(new_user) # 刷新对象状态 return new_userapp.get(/users/{user_id}, response_modelUserResponse)async def get_user(user_id: int, db: AsyncSession Depends(get_db)): 获取单个用户 result await db.execute(select(User).where(User.id user_id)) user result.scalar_one_or_none() if not user: raise HTTPException(status_code404, detailUser not found) return userapp.get(/users/, response_modelList[UserResponse])async def list_users(skip: int 0, limit: int 10, db: AsyncSession Depends(get_db)): 分页获取用户列表 result await db.execute( select(User).offset(skip).limit(limit) ) users result.scalars().all() return users## Alembic 数据库迁移配置### 踩坑点 4异步引擎与 Alembic 的兼容性Alembic 默认使用同步引擎需要手动配置异步支持。以下是一个完整的迁移配置python# alembic/env.py - 关键配置部分from logging.config import fileConfigfrom sqlalchemy import engine_from_configfrom sqlalchemy import poolfrom alembic import contextimport asynciofrom sqlalchemy.ext.asyncio import create_async_engine# 导入你的 Base 和模型from database import Basefrom models import User # 确保模型被导入config context.configfileConfig(config.config_file_name)target_metadata Base.metadatadef run_migrations_offline(): 离线迁移模式 url config.get_main_option(sqlalchemy.url) context.configure( urlurl, target_metadatatarget_metadata, literal_bindsTrue, dialect_opts{paramstyle: named}, ) with context.begin_transaction(): context.run_migrations()def do_run_migrations(connection): 执行迁移的核心函数 context.configure( connectionconnection, target_metadatatarget_metadata ) with context.begin_transaction(): context.run_migrations()async def run_async_migrations(): 异步迁移入口关键修复 connectable create_async_engine( config.get_main_option(sqlalchemy.url), poolclasspool.NullPool, ) async with connectable.connect() as connection: await connection.run_sync(do_run_migrations) await connectable.dispose()def run_migrations_online(): 在线迁移模式使用异步 asyncio.run(run_async_migrations())if context.is_offline_mode(): run_migrations_offline()else: run_migrations_online()### 迁移命令示例bash# 初始化 Alembicalembic init alembic# 生成迁移脚本alembic revision --autogenerate -m create_users_table# 执行迁移alembic upgrade head# 回滚alembic downgrade -1## 踩坑总结与最佳实践### 常见问题速查表| 问题现象 | 原因 | 解决方案 ||---------|------|----------||AsyncSession对象未关闭 | 忘记使用async with| 始终使用上下文管理器 || 事务提交后对象属性为 None |expire_on_commitTrue| 设置为 False 或手动 refresh || 异步查询返回空结果 | 忘记await| 检查所有数据库操作前加 await || Alembic 迁移报连接错误 | 使用了同步引擎 | 配置异步引擎适配器 || 外键约束错误 | 模型定义顺序问题 | 确保关联模型先定义 |### 性能优化建议1.连接池配置根据并发量调整pool_size和max_overflow2.懒加载 vs 立即加载使用selectinload避免 N1 查询3.批量操作使用bulk_insert_mappings提高插入性能4.索引优化为常用查询字段添加索引### 最终可运行示例将以上代码整合后运行以下命令即可体验完整功能bash# 1. 创建虚拟环境python -m venv venvsource venv/bin/activate# 2. 安装依赖pip install fastapi uvicorn sqlalchemy alembic asyncpg pydantic# 3. 启动服务uvicorn main:app --reload# 4. 访问 API 文档# http://localhost:8000/docs## 总结通过本文我们从零搭建了一套完整的 FastAPI SQLAlchemy 2.0 Alembic 技术栈涵盖了环境配置、模型定义、异步 CRUD 操作、数据库迁移等关键环节。踩坑记录揭示了异步编程中常见的陷阱包括上下文管理、事务状态、引擎类型选择等。记住异步不是魔法而是需要严谨对待的编程范式。在实际项目中建议结合单元测试和集成测试来验证数据库操作的正确性同时关注 SQLAlchemy 2.0 的官方文档更新因为异步 API 仍在持续优化中。掌握这套技术栈你将能高效构建健壮的后端系统。

相关新闻

短剧全AI译制流程靠谱吗?实测出错后的补救机制

短剧全AI译制流程靠谱吗?实测出错后的补救机制

全AI流程的"靠谱"不在于永不出错,而在于出错后有没有低成本的补救机制。本文实测常见出错场景,逐一列出对应补救路径,而非空谈"零失误"。一、全AI流程的可靠性基础:核心环节已达规模化生产标准判断一个全AI流…

2026/10/11 6:31:06 阅读更多 →
Hyper-V虚拟Openwrt软路由拨号后局域网内设备访问上级光猫的方法

Hyper-V虚拟Openwrt软路由拨号后局域网内设备访问上级光猫的方法

在笔者的另一篇文章《Hyper-V安装lede软路由历程及注意事项》中详细叙述了我在Hyper-V中安装lede(openwrt)软路由的经历。当时部署是通过dhcp方式从上级路由获取ip上网的,由于上级路由本身就可以访问光猫,所以软路由下的设备自然而…

2026/10/10 9:13:30 阅读更多 →
2026年国内展厅设计公司推荐:五大头部展厅设计公司深度评分与选型研判

2026年国内展厅设计公司推荐:五大头部展厅设计公司深度评分与选型研判

从案例积累、设计能力、数字技术、工程落地、综合服务五大维度分析,帮助企业选择适合的展厅设计合作伙伴随着企业品牌升级和数字化展示需求不断提升,展厅已经从传统意义上的“装修空间”发展成为集品牌传播、产品展示、企业文化输出、客户体验于一体的综…

2026/10/13 0:49:07 阅读更多 →

最新新闻

虚拟机+Linux入门:从安装配置到快照恢复的完整指南

虚拟机+Linux入门:从安装配置到快照恢复的完整指南

直接学Linux的人,十有八九第一堂课都是在装虚拟机。我自己当年学Linux,一开始直接拿一台老笔记本去装系统,结果分区的时候手一抖,把资料盘给格了,那种酸爽至今记忆犹新。后来老老实实按部就班用虚拟机,才发…

2026/10/13 16:54:42 阅读更多 →
SpringBoot+Vue3+MyBatis乡村政务办公系统开发与部署全解析

SpringBoot+Vue3+MyBatis乡村政务办公系统开发与部署全解析

身边不少朋友做过政务类的管理系统,我经手的项目里也有不少类似的诉求。乡村政务办公系统这类项目,看起来就是典型的CRUD加权限管理,但真正落地时,细节远比想象中多。尤其是面对乡、村两级的工作人员,系统不光要功能完…

2026/10/13 16:54:42 阅读更多 →
t3code:轻量级代码片段管理方案,纯文本与可检索的极简实践

t3code:轻量级代码片段管理方案,纯文本与可检索的极简实践

1. 项目缘起与核心定位第一次看到“t3code”这个名字,我下意识以为是某个新出的终端工具或者代码片段管理器。翻了一圈社区讨论和零散的项目描述之后才反应过来,它其实是一个面向轻量级代码片段与结构化文本的极简组织方案——你可以把它理解成“给开发者…

2026/10/13 16:54:42 阅读更多 →
用 CLI 终结团队代码规范之争:t3code 的设计与落地

用 CLI 终结团队代码规范之争:t3code 的设计与落地

大概半年前,我们团队在 Code Review 时的画风是这样的:有人坚持单引号,有人只用双引号;有人习惯把工具函数丢进utils.ts,有人喜欢按业务模块建helpers文件夹;新同事入职第一周,光适应"潜规…

2026/10/13 16:54:42 阅读更多 →
用Python打造Mercari新品扫描器:轻量爬虫与增量去重实战

用Python打造Mercari新品扫描器:轻量爬虫与增量去重实战

简介:mercari-scanner 是一套基于 Python 的 Mercari 新商品扫描与提醒工具,面向海淘代购人群和二手市场研究者,旨在解决手动刷新页面容易错过心仪商品的问题。工具支持通过命令行参数 --min-price 与 --max-price 设置价格区间,并…

2026/10/13 16:54:42 阅读更多 →
AI能力图谱日报:从热词到工程落地的决策指南

AI能力图谱日报:从热词到工程落地的决策指南

1. 这份“AI 日报”不是新闻简报,而是一份动态能力图谱很多人看到“AI 日报(2026年10月6日)”这个标题,第一反应是点开看今天又出了什么大模型、哪家公司发了新论文、又有哪个开源项目爆火了——就像刷微博热搜那样扫一眼就划走。…

2026/10/13 16:53:41 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →