Python FastAPI + SQLAlchemy 入门教程:从零搭建你的第一个 Web 应用

像搭积木一样,一步步构建完整的 Web 服务

📦 本文使用的代码文件

在开始之前,请先创建以下三个核心文件,所有代码都将基于它们展开:

复制代码
myproject/
├── main.py          # FastAPI 主入口(包含所有路由)
├── database.py      # 数据库配置(引擎、会话、依赖注入)
├── models.py        # ORM 模型(Category 和 Post)
└── seed.py          # 测试数据填充(可选)

前言

作为一名 Python 开发者,想要快速搭建一个 Web 应用,FastAPI + SQLAlchemy 的组合是目前最流行的选择之一。本文将用积木式的方式,带你从零开始,一步步搭建一个完整的博客文章管理系统。

你将学到:

  • ✅ FastAPI 路由、请求处理、响应
  • ✅ SQLAlchemy ORM 模型定义与数据库操作
  • ✅ Pydantic 数据校验
  • ✅ 依赖注入、中间件、启动事件
  • ✅ 完整的项目结构

前置知识: Python 基础语法、了解 HTTP 协议基本概念


第一块积木:数据库连接(database.py

📁 对应文件: database.py(完整代码见文末)

1.1 创建数据库引擎

python 复制代码
from sqlalchemy import create_engine

engine = create_engine(
    'mysql+pymysql://root:123456@localhost:3306/pythondb',
    echo=True,           # 打印执行的 SQL(开发调试用)
    pool_size=5,         # 连接池大小
    max_overflow=10      # 超出连接池后的最大连接数
)

# 测试连接
with engine.connect() as conn:
    print("连接成功!")

🔑 关键点:

  • 连接字符串格式:mysql+pymysql://用户名:密码@主机:端口/数据库名
  • 记得先在 MySQL 中创建对应的数据库 pythondb

1.2 创建会话工厂和基类

python 复制代码
from sqlalchemy.orm import sessionmaker, declarative_base

# 会话工厂:用于创建数据库会话
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

# ORM 基类:所有模型都继承它
Base = declarative_base()

💡 提示: 文件中还定义了一个 Base1 类,那是另一种写法示例,实际使用时直接用 Base 即可。

1.3 依赖注入获取数据库会话

python 复制代码
from fastapi import Depends

def get_db():
    """每个请求创建独立的数据库会话,请求结束后自动关闭"""
    db = SessionLocal()
    try:
        yield db   # 请求期间使用这个会话
    finally:
        db.close() # 防止连接泄漏

💡 为什么这样设计?

  • 每个请求使用独立的会话,互不干扰
  • 自动管理连接生命周期,避免资源泄漏
  • 配合 FastAPI 的依赖注入系统使用

第二块积木:数据模型(models.py

📁 对应文件: models.py(完整代码见文末)

定义数据库表结构,就像画施工图纸一样。

2.1 分类表(Category)

python 复制代码
from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey
from sqlalchemy.orm import relationship
from datetime import datetime
from database import Base

class Category(Base):
    """文章分类"""
    __tablename__ = "categories"
    
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String(50), unique=True, nullable=False)
    slug = Column(String(50), unique=True, nullable=False)
    
    # 与 Post 表建立双向关系
    posts = relationship("Post", back_populates="category")

2.2 文章表(Post)

python 复制代码
class Post(Base):
    """博客文章"""
    __tablename__ = "posts"
    
    id = Column(Integer, primary_key=True, index=True)
    title = Column(String(200), nullable=False)
    slug = Column(String(200), unique=True, nullable=False)
    summary = Column(Text, default="")
    content = Column(Text, nullable=False)
    
    category_id = Column(Integer, ForeignKey("categories.id"), nullable=False)
    created_at = Column(DateTime, default=datetime.utcnow)
    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
    
    # 与 Category 的双向关系
    category = relationship("Category", back_populates="posts")

📝 字段说明:

字段 类型 说明
id Integer 主键,自动索引
name String(50) 分类名称,唯一
slug String(200) URL 友好的标识符,必须唯一
summary Text 文章摘要,默认为空字符串
category_id Integer 外键,关联 categories 表
created_at DateTime 创建时间,自动填充当前时间
updated_at DateTime 更新时间,每次修改自动更新

第三块积木:Pydantic 数据校验

📁 对应位置: main.py

Pydantic 是 FastAPI 的数据校验核心,自动生成 API 文档 + 自动校验输入数据

3.1 基础模型

python 复制代码
from pydantic import BaseModel, EmailStr

class UserBase(BaseModel):
    username: str                    # 必填
    email: EmailStr                  # 必填,自动校验邮箱格式
    full_name: str | None = None     # 可选

3.2 输入输出分离

python 复制代码
# 创建用户时的输入模型(含密码)
class UserCreate(UserBase):
    password: str

# 返回用户信息的输出模型(不含密码)
class UserOut(UserBase):
    id: int

@app.post("/users/", response_model=UserOut)
async def create_user(user: UserCreate):
    # 响应时自动过滤密码字段
    return {"id": 1, **user.model_dump(exclude={"password"})}

最佳实践: 输入和输出模型分开定义,避免敏感信息泄露

3.3 商品模型示例

python 复制代码
class Item(BaseModel):
    name: str
    age: int = None   # 可选字段

@app.post("/items1")
def items1(item: Item):
    print(item.model_dump())       # 转为字典
    print(item.model_dump_json())  # 转为 JSON 字符串
    return item

第四块积木:FastAPI 路由(main.py

📁 对应位置: main.py

4.1 基础路由

python 复制代码
from fastapi import FastAPI

app = FastAPI()

# GET 请求,路径参数
@app.get("/")
async def root():
    return {"message": "Hello World"}

@app.get("/items/{item_id}")
def read_item(item_id: int, q1: str = None, p2: str = "p22"):
    return {"item_id": item_id, "q": q1, "p2": p2}

# POST 请求,接收 JSON Body
@app.post("/items1")
def items1(item: Item):
    return item
python 复制代码
from fastapi import Header, Cookie

@app.get("/item2")
def item2(
    user_agent: str = Header(None),
    session_token: str = Cookie(None)
):
    return {"User-Agent": user_agent, "Session-Token": session_token}

4.3 重定向和状态码

python 复制代码
from fastapi.responses import RedirectResponse, JSONResponse
from fastapi import HTTPException

# 重定向
@app.get("/redirect")
def redirect():
    return RedirectResponse(url="/item2/")

# 抛出 HTTP 异常
@app.get("/items1/{item_id}")
def read_item(item_id: int):
    if item_id == 42:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"item_id": item_id}

# 自定义响应头
@app.get("/default_header")
def default_header():
    return JSONResponse(
        content={"item_id": 1000, "body": 10001},
        headers={"X-Custom-Header": "custom-value"}
    )

4.4 表单数据处理

python 复制代码
from fastapi import Form

@app.post("/login/")
async def login(
    username: str = Form(),      # 必填
    password: str = Form(),      # 必填
):
    print(f"{password=}")        # 调试输出
    return {"username": username}

@app.post("/items/")
async def create_item(
    name: str = Form(...),              # ... 表示必填
    description: str | None = Form(None),
    price: float = Form(..., gt=0),     # 必须大于 0
):
    return {"name": name, "description": description, "price": price}

第五块积木:路由分组(APIRouter)

📁 对应位置: main.py

当路由越来越多时,用 APIRouter 进行模块化管理。

python 复制代码
from fastapi import APIRouter

router = APIRouter(
    prefix="/posts",      # 所有路由自动加上 /posts 前缀
    tags=["文章"]          # Swagger 文档分组
)

@router.get("/{post_id}", name="post_detail")
def post_detail(post_id: int):
    """文章详情"""
    return {"post_id": post_id}

# 挂载到主应用
app.include_router(router)

第六块积木:启动事件 - 自动建表

📁 对应位置: main.py

应用启动时自动创建数据库表,开发阶段非常方便。

python 复制代码
from database import engine, Base
from models import Category, Post  # 确保模型被导入

@app.on_event("startup")
def on_startup():
    """应用启动时自动创建数据库表(仅开发使用)"""
    Base.metadata.create_all(bind=engine)
    print("✅ 数据库表创建成功!")

⚠️ 注意: 如果表已存在,create_all 不会重复创建,只会创建不存在的表。


第七块积木:数据填充(seed.py

⚠️ 注意: seed.py 文件在提供的代码中未给出完整实现 ,但在 main.py 中被引用。

python 复制代码
# main.py 中的使用
import seed

@app.post("/insert_data")
async def insert_data():
    seed.exec()  # 执行数据填充
    return "success..."

你需要自己创建 seed.py 文件,示例内容:

python 复制代码
# seed.py - 你需要自行创建
from database import SessionLocal
from models import Category, Post

def exec():
    db = SessionLocal()
    try:
        # 创建分类
        cat = Category(name="Python", slug="python")
        db.add(cat)
        db.commit()
        
        # 创建文章
        post = Post(
            title="FastAPI 入门",
            slug="fastapi-intro",
            content="这是内容...",
            category_id=cat.id
        )
        db.add(post)
        db.commit()
        print("✅ 测试数据填充成功!")
    finally:
        db.close()

第八块积木:完整代码汇总

📄 database.py(完整)

python 复制代码
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
from fastapi import Depends

# MySQL连接字符串
engine = create_engine(
    'mysql+pymysql://root:123456@localhost:3306/pythondb',
    echo=True,
    pool_size=5,
    max_overflow=10
)

# 测试连接
with engine.connect() as conn:
    print("连接成功!")

# 会话工厂
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

# ORM 基类
Base = declarative_base()

# 依赖注入
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

📄 models.py(完整)

python 复制代码
from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey
from sqlalchemy.orm import relationship
from datetime import datetime
from database import Base

class Category(Base):
    __tablename__ = "categories"
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String(50), unique=True, nullable=False)
    slug = Column(String(50), unique=True, nullable=False)
    posts = relationship("Post", back_populates="category")

class Post(Base):
    __tablename__ = "posts"
    id = Column(Integer, primary_key=True, index=True)
    title = Column(String(200), nullable=False)
    slug = Column(String(200), unique=True, nullable=False)
    summary = Column(Text, default="")
    content = Column(Text, nullable=False)
    category_id = Column(Integer, ForeignKey("categories.id"), nullable=False)
    created_at = Column(DateTime, default=datetime.utcnow)
    updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
    category = relationship("Category", back_populates="posts")

📄 main.py(核心部分)

python 复制代码
from fastapi import FastAPI, Depends, Header, Cookie, Form, HTTPException
from fastapi.responses import RedirectResponse, JSONResponse
from fastapi import APIRouter
from pydantic import BaseModel, EmailStr
import database, models, seed

app = FastAPI()

# ... 所有路由定义(参考第四、五部分)...

@app.on_event("startup")
def on_startup():
    models.Base.metadata.create_all(bind=database.engine)

# 挂载路由
app.include_router(router)

快速启动

bash 复制代码
# 1. 安装依赖
pip install fastapi uvicorn sqlalchemy pymysql pydantic

# 2. 创建数据库(MySQL)
mysql -u root -p
CREATE DATABASE pythondb;

# 3. 启动服务(热重载模式)
uvicorn main:app --reload

# 4. 访问交互式 API 文档
# http://127.0.0.1:8000/docs

总结:积木清单

积木 文件 作用 状态
🧱 数据库引擎 database.py 连接数据库 ✅ 已提供
🧱 会话工厂 database.py 管理数据库会话 ✅ 已提供
🧱 依赖注入 database.py 获取数据库会话 ✅ 已提供
🧱 ORM 模型 models.py 定义表结构 ✅ 已提供
🧱 Pydantic 模型 main.py 数据校验 ✅ 已提供
🧱 路由定义 main.py API 端点 ✅ 已提供
🧱 路由分组 main.py 模块化管理 ✅ 已提供
🧱 启动事件 main.py 自动建表 ✅ 已提供
🧱 数据填充 seed.py 测试数据 ⚠️ 需自行创建

⚠️ 注意事项

  1. seed.py 文件未提供main.py 中引用了 import seedseed.exec(),你需要自行创建该文件,或注释掉相关路由。

  2. 修改数据库配置 :将 database.py 中的连接字符串改为你自己的 MySQL 配置。

  3. 生产环境注意

    • 关闭 echo=True
    • 不要在生产环境使用 create_all()
    • 使用环境变量管理敏感信息

下一步可以学什么?

  1. 关联查询 :学习 joinedload 预加载关联数据
  2. 分页查询 :实现 offset / limit 分页
  3. 中间件:添加日志、CORS、认证
  4. 异步支持 :使用 async / await 处理高并发
  5. 单元测试 :用 TestClient 测试 API

💡 记住: 编程就像搭积木,先把基础模块理解透彻,再逐步组合成复杂的系统。本文的每个代码块都是独立的积木,你可以随时取用!

如果你在实践过程中遇到问题,欢迎在评论区留言交流! 🚀

相关推荐
林小帅1 小时前
NestJS v11 + Prisma v7 ESM 迁移配置解析
前端·javascript·后端
IT_陈寒2 小时前
React的setState竟然不是立刻生效的,害我调试半天
前端·人工智能·后端
简简单单就是我_hehe2 小时前
前端埋点日志解决方案:完整 User-Agent 解析工具实现,爬虫识别、设备品牌、操作系统、浏览器版本全解析(附完整源码)
前端·爬虫
William_Xu2 小时前
Vue 2 与 Vue 3 生命周期对比
前端
Hilaku3 小时前
为什么你的大文件断点续传,一遇到弱网就崩溃?
前端·javascript·程序员
胡哈4 小时前
React Compiler:让开发者忘掉 memo 这件事
前端·react.js
xiyueyezibile4 小时前
“自迭代” Skill 的 team-pitfalls 的演进
前端·后端·ai编程
北极星日淘4 小时前
中古货品品相评级算法实战|Java权重计分实现标准化五级品相体系
开发语言·python
hangyuekejiGEO4 小时前
临沂GEO服务企业技术选型分析
人工智能·python