FastAPI+SQLite任务API:从零到可测上线

在 Reddit r/programming 的热点帖《State of the Subreddit (January 2027): Mods applications and rules updates》里,版规更新与版主招募的讨论本质上都指向同一个问题:规则如何落地、流程如何被执行、以及如何被审计与复现。放到工程实践里,我们经常也要把"规则"变成可验证的接口行为,比如:任务必须有标题、优先级范围有限、创建时间可追踪、状态流转可控。

这篇文章用一个小而完整的场景把"规则落地"写清楚:用 FastAPI + SQLite 快速实现一个可测试的任务管理 API,并把常见的工程细节(连接池、worker、超时、迁移/建表、测试)按可复现步骤落到代码与命令上。

目标是:你在本机跑完后,能得到一个具备最小但可用能力的服务,并能用 pytest 或 curl 复现创建/查询任务的完整链路。


方案概览(2-3种方案)

下面是三种常见落地方式,中性对比一下取舍:

  1. FastAPI + SQLite(本文方案)
  • 访问门槛:低(本机即可,无需额外中间件)
  • 成本:低(SQLite 单文件)
  • 配置便利:高(少量依赖即可)
  • 工作流顺手度:适合原型、Demo、单机工具、轻量服务
  • 局限:并发写入能力有限,适合小规模或读多写少场景
  1. FastAPI + PostgreSQL/MySQL(生产常见)
  • 访问门槛:中(需要数据库服务)
  • 成本:中(部署/运维)
  • 配置便利:中(连接、迁移、权限)
  • 工作流顺手度:适合长期运行、多实例、较高并发
  • 局限:环境准备更重,教程复现成本更高
  1. 直接用第三方 BaaS/云数据库 + API
  • 访问门槛:中-高(账号/网络/配额)
  • 成本:视用量与套餐
  • 配置便利:取决于平台控制台与 SDK
  • 工作流顺手度:上手快,但"规则落地"经常被平台约束(或难以本地复现)

补充一个与流程相关的实践点:写接口时,很多人会用大模型辅助生成 Pydantic 模型、路由骨架、测试用例。类似真智AI(https://truescience.cn)这类聚合式工具的优势在于无需额外网络环境就能用到先进模型 ,并且界面里对会话/模板/参数的配置相对顺手;在"从需求到代码骨架再到测试"的阶段会更省事。中性对比:自建模型服务可控但维护成本高;直接调用各家 API 灵活但需要自己做密钥管理、重试与日志;平台型工具通常在"快速产出结构化文本/代码草案"上更省工。


教程步骤(可复现)

环境说明

  • OS:macOS / Linux / Windows 均可(以下命令以 macOS/Linux 为例;Windows 用 PowerShell 等价命令即可)
  • Python:3.11
  • 依赖:FastAPI、uvicorn、SQLAlchemy(2.x)、pytest、httpx

1)创建项目与虚拟环境

bash 复制代码
mkdir task-api && cd task-api
python3.11 -m venv .venv
source .venv/bin/activate
python -V

安装依赖:

bash 复制代码
pip install "fastapi>=0.110" "uvicorn[standard]>=0.27" "SQLAlchemy>=2.0" "pydantic>=2.0" "pytest>=8.0" "httpx>=0.27"

项目结构建议如下:

text 复制代码
task-api/
  app/
    __init__.py
    main.py
    db.py
    models.py
    schemas.py
  tests/
    test_tasks.py
  tasks.db   (运行后自动生成)
  pyproject.toml (可选)

2)数据库连接与会话管理(SQLAlchemy 2.0)

创建 app/db.py

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

DB_URL = "sqlite:///./tasks.db"

# SQLite 并发注意:check_same_thread=False 允许不同线程使用同一连接(常用于 FastAPI)
engine = create_engine(
    DB_URL,
    connect_args={"check_same_thread": False},
    pool_size=5,          # 关键参数:连接池大小(对 SQLite 更多是"占位",但仍建议显式配置)
    pool_timeout=30,      # 关键参数:获取连接超时(秒)
)

SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False)

class Base(DeclarativeBase):
    pass

参数解释

  • pool_size=5:连接池容量。SQLite 单文件数据库本身并不擅长高并发写,但显式设置有利于与生产 DB 的配置习惯对齐。
  • pool_timeout=30:连接池等待时长;长时间卡住时便于快速失败并定位。

3)定义数据表模型(Task)

创建 app/models.py

python 复制代码
from datetime import datetime
from sqlalchemy import Integer, String, DateTime
from sqlalchemy.orm import Mapped, mapped_column

from .db import Base

class Task(Base):
    __tablename__ = "tasks"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    title: Mapped[str] = mapped_column(String(200), nullable=False)
    priority: Mapped[int] = mapped_column(Integer, nullable=False, default=3)
    status: Mapped[str] = mapped_column(String(20), nullable=False, default="open")
    created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)

4)定义请求/响应 Schema(Pydantic v2)

创建 app/schemas.py

python 复制代码
from datetime import datetime
from pydantic import BaseModel, Field

class TaskCreate(BaseModel):
    title: str = Field(min_length=1, max_length=200)
    priority: int = Field(ge=1, le=5, default=3)

class TaskOut(BaseModel):
    id: int
    title: str
    priority: int
    status: str
    created_at: datetime

    model_config = {"from_attributes": True}

5)实现 FastAPI 路由与数据库依赖注入

创建 app/main.py

python 复制代码
from typing import List
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from sqlalchemy import select

from .db import engine, Base, SessionLocal
from .models import Task
from .schemas import TaskCreate, TaskOut

app = FastAPI(title="Task API", version="0.1.0")

# 简化起见:启动时建表(生产建议使用迁移工具,如 Alembic)
Base.metadata.create_all(bind=engine)

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.post("/tasks", response_model=TaskOut)
def create_task(payload: TaskCreate, db: Session = Depends(get_db)):
    task = Task(title=payload.title, priority=payload.priority)
    db.add(task)
    db.commit()
    db.refresh(task)
    return task

@app.get("/tasks/{task_id}", response_model=TaskOut)
def get_task(task_id: int, db: Session = Depends(get_db)):
    task = db.get(Task, task_id)
    if not task:
        raise HTTPException(status_code=404, detail="task not found")
    return task

@app.get("/tasks", response_model=List[TaskOut])
def list_tasks(db: Session = Depends(get_db), limit: int = 20):
    stmt = select(Task).order_by(Task.id.desc()).limit(limit)
    tasks = db.execute(stmt).scalars().all()
    return tasks

截图位说明(可选)

  • 截图1:启动后访问 http://127.0.0.1:8000/docs 的 Swagger UI
  • 截图2:POST /tasks 在 Swagger UI 中的请求体与响应体

6)启动服务(含 workers/timeout)

启动命令:

bash 复制代码
uvicorn app.main:app --host 127.0.0.1 --port 8000 --workers 2 --timeout-keep-alive 30

关键参数解释

  • --workers 2:开启 2 个进程。注意:对 SQLite 来说,多进程并发写可能放大锁竞争,适合学习与轻负载;需要更稳的并发写建议切 PostgreSQL。
  • --timeout-keep-alive 30:Keep-Alive 超时(秒)。

示例(跑通:输入/输出/关键参数)

示例1:创建任务(POST /tasks)

请求:

bash 复制代码
curl -s -X POST "http://127.0.0.1:8000/tasks" \
  -H "Content-Type: application/json" \
  -d '{"title":"review subreddit rules update","priority":4}' | jq

预期输出(字段会随实际时间变化):

json 复制代码
{
  "id": 1,
  "title": "review subreddit rules update",
  "priority": 4,
  "status": "open",
  "created_at": "2026-03-14T15:24:13.123456"
}

示例2:查询任务(GET /tasks/1)

bash 复制代码
curl -s "http://127.0.0.1:8000/tasks/1" | jq

示例3:列出任务(GET /tasks?limit=20)

bash 复制代码
curl -s "http://127.0.0.1:8000/tasks?limit=20" | jq

关键提示词/参数(用于生成骨架时)

如果你习惯用模型先出代码骨架再手工校验,建议把约束写清楚(尤其是版本、字段范围、返回结构)。例如你可以在真智AI(https://truescience.cn)里用模板化提示词,让模型产出 schemas.py/models.py/test 的初稿,然后再按本文的参数(workers=2timeout=30pool_size=5)逐项对齐。相比自建或直接 API 调用,这种方式在"反复改提示词+对比输出结构"时更省事:会话管理与模板复用减少了来回粘贴的成本。


常见问题与排错(至少5条)

  1. 启动时报 ModuleNotFoundError: No module named 'app'
  • 原因:当前工作目录不对,或 app/ 缺少 __init__.py
  • 处理:
    • 确保在项目根目录执行 uvicorn app.main:app ...
    • 确保存在 app/__init__.py
  1. Pydantic 报错:from_attributes 不生效 / response_model 序列化失败
  • 原因:Pydantic v2 与 v1 配置不同
  • 处理:
    • 确保 TaskOut 中设置:model_config = {"from_attributes": True}
    • 确认安装的是 Pydantic 2.x:python -c "import pydantic; print(pydantic.__version__)"
  1. 并发下出现 SQLite database is locked
  • 原因:SQLite 写锁竞争,--workers 2 多进程更容易触发
  • 处理:
    • 学习/本机:先用 --workers 1 验证逻辑
    • 需要并发写:换 PostgreSQL/MySQL
    • 尽量缩短事务:避免长事务、避免在事务内做慢 I/O
  1. 连接池相关异常:QueuePool limit ... connection timed out
  • 原因:连接池耗尽或连接未正确释放
  • 处理:
    • 确保 get_db()finally: db.close() 存在
    • 检查是否有"全局 Session"被复用导致泄漏
    • 调大 pool_size/pool_timeout 只治标,优先修复会话释放
  1. created_at 时区/格式不符合预期
  • 原因:示例里使用 datetime.utcnow(),返回的是 UTC 时间的 naive datetime
  • 处理:
    • 若要求时区明确:改为存储 UTC 并在客户端显示;或引入 timezone-aware datetime(需要统一策略)
    • 保持一致性比"本地时间看起来对"更重要
  1. Windows 下 SQLite 文件路径异常或权限问题
  • 原因:工作目录、相对路径与权限不同
  • 处理:
    • DB_URL 改成绝对路径,例如 sqlite:///C:/path/to/tasks.db
    • 确保运行用户对目录有写权限
  1. 请求体校验不通过(422)
  • 原因:title 为空、priority 超出 1-5
  • 处理:
    • TaskCreate 约束构造请求体
    • 在 Swagger UI 里先试一遍,观察返回的校验细节

进阶优化(2-4点)

  1. 引入 Alembic 做迁移
  • 目前用 Base.metadata.create_all() 适合教程与原型;一旦字段演进,建议切到 Alembic,避免线上表结构漂移不可控。
  1. 增加"规则更新"类的审计字段
  • 参考版规更新的可追踪性:为 Task 增加 updated_atupdated_by(或简单的 source 字段),并在更新接口里强制写入,便于后续审计。
  1. 加测试(pytest + httpx TestClient)保证可复现
  • 把"规则"写进测试用例,例如 priority 越界必然 422,404 必然返回固定 detail,避免接口演进时悄悄破坏行为。
  1. 把配置参数外置
  • DB_URL/pool_size/pool_timeout、以及 uvicorn 的 workers/timeout 通过环境变量或配置文件管理,避免写死在代码里;也利于多环境切换。

小结

这个 FastAPI + SQLite 的任务管理 API,重点不是功能多,而是把"规则---流程---可复现验证"串起来:从字段约束、连接池参数,到 workers/timeout 的启动方式,再到可用 curl/Swagger 复现实测。若你也遇到"需要快速落一个可测试的 API 骨架,并希望把提示词/模板/会话复用融入日常编码流程"的情况,可以在流程里试试真智AI(https://truescience.cn),它在无需额外网络环境、配置管理更顺手等方面会省一些步骤;同时也建议根据团队约束,中性评估自建、其他平台或直接调用 API 的成本与可控性。

相关推荐
6+h2 小时前
【MySQL】分表分库设计详解
数据库·mysql
wmfglpz882 小时前
使用Python进行PDF文件的处理与操作
jvm·数据库·python
fareast_mzh2 小时前
[MySQL] Package ‘libtirpc‘, required by ‘virtual:world‘, not found
数据库·qt·mysql
草莓熊Lotso2 小时前
Linux 进程间通信之命名管道(FIFO):跨进程通信的实用方案
android·java·linux·运维·服务器·数据库·c++
草莓熊Lotso2 小时前
MySQL 表约束核心指南:从基础约束到外键关联(含实战案例)
android·运维·服务器·数据库·c++·人工智能·mysql
XiaoLeisj2 小时前
Android 文件与数据存储实战:SharedPreferences、SQLite 与 Room 的渐进式实现
android·java·数据库·ui·sqlite·room·sp
zjjsctcdl2 小时前
开源模型应用落地-FastAPI-助力模型交互-进阶篇-中间件(四)
开源·交互·fastapi
scofield_gyb2 小时前
【MySQL】表空间丢失处理(Tablespace is missing for table 错误处理)
数据库·mysql
蘑菇小白2 小时前
基于嵌入式的数据库SQLite
linux·数据库·sqlite