Add page-tree decision and Wagtail M2M implementation note

- 04-architecture 3.1: add ProjectIndexPage; authoritative page tree
  (project pages live under /projects/, never under scenario pages);
  ScenarioPage renders projects via query; filtering lives in
  ProjectIndexPage.get_context(), no separate Django view; Page M2M
  fields must use modelcluster ParentalManyToManyField
- 06-tasks: sync T-102 row/detail and T-204 quick-table accordingly
- routes: point project list page back to the 3.1 tree
- progress.md: append maintenance record

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
ila
2026-07-06 16:23:44 +08:00
co-authored by Claude Fable 5
parent 678d081124
commit 14eae57643
4 changed files with 37 additions and 2 deletions
+4 -2
View File
@@ -31,7 +31,7 @@
| ID | 任务 | 依赖 | 验收要点 | 状态 |
| --- | --- | --- | --- | --- |
| T-101 | 建立场景、语言、框架、数据库、功能标签模型 | T-003 | Wagtail 后台可维护这些 Snippet;迁移文件生成并通过 migrate | TODO |
| T-102 | 建立骨架项目详情模型 | T-101 | `SkeletonProjectPage` 严格按 `04-architecture.md` §3.3 表和必填/默认附注实现;评分字段带 0-5 校验器;总分由分项计算 | TODO |
| T-102 | 建立骨架项目详情模型 | T-101 | `SkeletonProjectPage` 严格按 `04-architecture.md` §3.3 表和必填/默认附注实现;多对多用 `ParentalManyToManyField`;同时建 `ProjectIndexPage`;评分字段带 0-5 校验器;总分由分项计算 | TODO |
| T-103 | 建立文章模型 | T-101 | 可维护文章列表和详情;文章可链接骨架项目 | TODO |
| T-104 | 准备首批种子内容 | T-102, T-103 | 至少有 3 个场景、5 个骨架项目、2 篇文章;候选项目、评分和评语须经人工确认,禁止模型编造(见详单) | TODO |
@@ -101,6 +101,8 @@
### T-102 骨架项目详情模型
- `SkeletonProjectPage`(`core` app):字段、必填/可空/默认严格按 `04-architecture.md` §3.3 表和附注实现,不增不减。
- **多对多字段(`scenarios`、`languages`、`frameworks`、`databases`、`features`)必须用 modelcluster 的 `ParentalManyToManyField`**,不能用普通 `ManyToManyField`(见 §3.1 Wagtail 实现注意)。
- 同时建 `ProjectIndexPage`(项目详情页在页面树上的父节点,见 §3.1 页面树;本轮只建模型,筛选逻辑留给 T-204)。
- 6 个评分字段带 `MinValueValidator(0)` / `MaxValueValidator(5)`;`total_score` 为 property(6 项之和,0-30)。
- 后台按 5 组分面板:基本信息 / 分类标签 / 评分 / 评测内容 / 展示控制。
- 验证单元测试至少 3 个:合法对象 `full_clean()` 通过;某评分设为 6 时 `full_clean()` 抛 `ValidationError`;`total_score` 等于 6 项之和。`manage.py test` 全部通过。
@@ -124,7 +126,7 @@
| T-201 | `base.html` 含 `<meta name="viewport">`;纯 CSS 无构建链路;无超视口固定宽度容器 | `curl /` 返回 200;各页面模板均继承 `base.html` |
| T-202 | 首页三区块全部来自数据库查询(`is_featured`、最新文章),不硬编码内容 | 测试断言 featured 项目标题出现在 `/` 响应中 |
| T-203 | 场景页只列已发布(live)项目;分页每页 20;空场景渲染 `empty_state.html` | 测试两用例:有项目场景(200 且含项目名)、空场景(200 且含空状态文案) |
| T-204 | 严格按 `04-architecture.md` §3.4 契约;搜索用 ORM `icontains` | 测试 4 用例:`language` 过滤、`min_score` 过滤、`q` 搜索、非法参数被忽略 |
| T-204 | 严格按 `04-architecture.md` §3.4 契约;筛选实现在 `ProjectIndexPage.get_context()`;搜索用 ORM `icontains` | 测试 4 用例:`language` 过滤、`min_score` 过滤、`q` 搜索、非法参数被忽略 |
| T-205 | 展示 §3.3 全部展示字段;`is_sponsored=True` 时渲染 `sponsored_badge.html` | 测试断言详情页含评分、推荐理由;赞助用例含 Sponsored 标识 |
| T-206 | 文章详情可点击进入关联项目详情页 | 测试断言文章页响应含项目详情链接 |
| T-301 | title/description 用 Wagtail 自带 `seo_title` / `search_description`;sitemap 用 `wagtail.contrib.sitemaps` | `curl /sitemap.xml` 返回 200 且含项目 URL;首页响应含 `<meta name="description"` |