Add Alembic migration foundation
test / pytest (push) Successful in 1m34s

This commit is contained in:
2026-06-01 16:02:43 +02:00
parent c42cc2ddb6
commit 8b8bd9f38f
17 changed files with 1459 additions and 101 deletions
+10 -8
View File
@@ -208,16 +208,18 @@ The service worker only claims clients — **no caching, no offline** yet.
---
## 10. 数据库初始化与迁移 / DB Init & Migrations (`app/db.py`)
## 10. 数据库初始化与迁移 / DB Init & Migrations (`app/migrate.py` + `app/db.py`)
- 懒加载 engine`init_db()` 在 FastAPI `lifespan` 启动时调用,执行 `Base.metadata.create_all`
Lazy engine; `init_db()` runs at FastAPI startup (lifespan) and does `create_all`.
- **Alembic 接管 schema**:迁移系统由 Alembic 管理(`alembic.ini` + `migrations/`),V1 baseline 等于当前三表 schema
Alembic owns schema creation and changes (`alembic.ini` + `migrations/`); V1 baseline equals the current three-table schema.
- **迁移与启动分离 / Migrations separated from startup**
- `init_db()``app/db.py`)在 FastAPI lifespan 启动时调用 `verify_schema_is_current()`,只做**只读校验**——检查 DB 是否在 `head`,不一致则 **fail-close**(拒绝启动、不执行任何 DDL)。
`init_db()` calls `verify_schema_is_current()` at startup — read-only check, fails closed on mismatch, no DDL.
- 实际迁移由独立幂等命令 `python -m app.migrate``app/migrate.py`)执行:空库建表、老库认领(stamp V1 → upgrade head)、已在 head 则空操作。老库 schema 不匹配则 fail-close 不改动。
Actual migration via standalone idempotent command `python -m app.migrate`: fresh DB → create, matching existing → adopt, already-at-head → no-op, mismatch → fail closed.
- SQLite 连接开启 `PRAGMA foreign_keys=ON`
- **轻量「迁移」/ Ad-hoc migration**`_sync_sqlite_image_columns()` 在启动时用 `PRAGMA table_info` 检测,并对缺失的图片列做 `ALTER TABLE ADD COLUMN`。这是项目**唯一**的迁移机制,专门为「后加图片功能」补列而写
The only migration mechanism is a hand-written check that adds the four image columns if missing.
> ⚠️ **没有 Alembic / 没有通用迁移**。新增任何**非图片**字段到已有库,需要扩展这段逻辑或手动 `ALTER`,否则旧库不会自动获得新列。这是下一轮改动需要特别注意的点(见 §14)。
> No Alembic / general migrations — adding new non-image columns to an existing DB needs manual handling.
- 手写列同步 `_sync_sqlite_image_columns()` 已退休删除
The hand-rolled `_sync_sqlite_image_columns()` has been retired and removed.
---