FastAPI + PostgreSQL 实战:从入门到不踩坑,一次讲透

🧐 第一部分:为什么是PostgreSQL?

你可以把PostgreSQL想象成一个**"极度守规矩的档案管理员"**------数据完整性、ACID、复杂查询支持得滴水不漏。

相比MySQL,它对JSON、全文检索、地理空间数据的支持更原生,而且这几年性能优化得特别猛。

最关键的是,它和FastAPI的异步生态配合得非常好,后面你会体会到。

⚙️ 第二部分:安装PostgreSQL(Docker一键搞定)

最怕那种"双击安装包一路下一步"然后环境变量配半小时的教程。咱们用Docker,干净利落。

复制代码
docker run -d \
  --name postgres-demo \
  -e POSTGRES_PASSWORD=mysecretpassword \
  -e POSTGRES_USER=myuser \
  -e POSTGRES_DB=mydb \
  -p 5432:5432 \
  postgres:15

然后你就可以用任何客户端(比如TablePlusDBeaver )连上localhost:5432验证一下。记得密码别写死在代码里,后面咱们用环境变量。

敲黑板: 本地开发千万别用root用户,上面我特意建了普通用户myuser,权限最小化原则。

📦 第三部分:FastAPI项目初始化

创建一个新目录,弄个虚拟环境,然后装依赖。这里我推荐两个库

asyncpg ------ PostgreSQL的异步驱动,性能爆表。

databases ------ 封装了连接池和查询,让你能用async/await执行SQL,还支持SQLAlchemy Core(可选)。

当初我图省事直接用了同步的psycopg2,结果FastAPI异步线程被阻塞,性能直接打骨折。所以,一定一定要用异步驱动!

安装命令:

复制代码
pip install fastapi uvicorn databases[asyncpg] python-dotenv

💻 第四部分:写代码,让FastAPI和PG连上线

先建一个.env文件存放敏感信息:

复制代码
DATABASE_URL=postgresql://myuser:mysecretpassword@localhost/mydb

然后写主程序main.py

复制代码
import os
from fastapi import FastAPI
from contextlib import asynccontextmanager
from databases import Database
from dotenv import load_dotenv

load_dotenv()

DATABASE_URL = os.getenv("DATABASE_URL")
database = Database(DATABASE_URL)

@asynccontextmanager
async def lifespan(app: FastAPI):
    await database.connect()
    # 创建表(实际项目用Alembic,这里演示直接执行SQL)
    query = """CREATE TABLE IF NOT EXISTS users (
        id SERIAL PRIMARY KEY,
        username VARCHAR(50) UNIQUE NOT NULL,
        email VARCHAR(100) UNIQUE NOT NULL
    );"""
    await database.execute(query=query)
    print('数据表创建完成!')
    yield
    await database.disconnect()

app = FastAPI(lifespan=lifespan)

@app.get("/users")
async def get_users():
    query = "SELECT id, username, email FROM users"
    results = await database.fetch_all(query=query)
    return [dict(r) for r in results]

@app.post("/users")
async def create_user(username: str, email: str):
    # 简单的插入,实际要加异常处理
    query = "INSERT INTO users(username, email) VALUES (:username, :email) RETURNING id"
    values = {"username": username, "email": email}
    user_id = await database.execute(query=query, values=values)
    return {"id": user_id, "username": username, "email": email}

启动服务:uvicorn main:app --reload。打开http://localhost:8000/docs,你就能看到自动生成的API文档,试一下POST和GET,是不是很爽?

⚠️ 第五部分:那些年我踩过的坑(精华)

🚨 坑1:连接被拒绝

现象:ConnectionRefusedError。大概率是Docker容器端口没映射出来,或者PostgreSQL配置只绑定了127.0.0.1。用Docker一定要加-p 5432:5432。另外检查防火墙。

🚨 坑2:asyncpg.exceptions.InvalidPasswordError

密码错误?其实很多时候是因为URL格式不对。注意postgresql://user:pass@host/db,特殊字符要URL编码。

🚨 坑3:在异步函数里用了同步的psycopg2连接

这会导致事件循环阻塞,性能急剧下降。解决方案就是全程用asyncpgdatabases

🚨 坑4:事务忘记commit?

database.transaction()上下文管理器最安全:

复制代码
async with database.transaction():
    await database.execute(query1)
    await database.execute(query2)  # 如果出错,自动回滚

🚨 坑5:连接池耗尽

默认连接池大小可能不够,高并发时需调整:

复制代码
database = Database(DATABASE_URL, min_size=5, max_size=20)
相关推荐
黄焖鸡能干四碗3 小时前
固定资产管理系统建设方案和源码(Java源码)
大数据·数据库·人工智能·物联网·区块链
JoneBB4 小时前
ABAP Webservice连接
运维·开发语言·数据库·学习
解决问题no解决代码问题4 小时前
从乱码到脱敏导出:TiDB CSV 导出实战全指南
数据库
未若君雅裁4 小时前
MySQL高可用与扩展-主从复制读写分离分库分表
java·数据库·mysql
2401_867623984 小时前
CSS Flex布局中如何设置子元素间距_掌握gap属性的现代用法
jvm·数据库·python
月落归舟4 小时前
一篇文章了解Redis内存淘汰机制与过期Key清理
数据库·redis·mybatis
phltxy5 小时前
Redis 事务
数据库·redis·缓存
康乾隆5 小时前
SQL Server Always On 重新添加从库步骤
数据库·sqlserver
环流_6 小时前
redis核心数据类型在java中的操作
java·数据库·redis
雨辰AI6 小时前
SpringBoot3 项目国产化改造完整流程|从 MySQL 到人大金仓落地
java·数据库·后端·mysql·政务