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:
@@ -61,12 +61,32 @@ SQLite db.sqlite3
|
|||||||
| `HomePage` | Page | 首页,展示场景入口、推荐骨架、最新文章。 |
|
| `HomePage` | Page | 首页,展示场景入口、推荐骨架、最新文章。 |
|
||||||
| `ScenarioIndexPage` | Page | 场景总览页。 |
|
| `ScenarioIndexPage` | Page | 场景总览页。 |
|
||||||
| `ScenarioPage` | Page | 单个场景页,如 SaaS、管理后台、API 服务。 |
|
| `ScenarioPage` | Page | 单个场景页,如 SaaS、管理后台、API 服务。 |
|
||||||
|
| `ProjectIndexPage` | Page | 项目列表页(`/projects/`),承载筛选与分页。 |
|
||||||
| `SkeletonProjectPage` | Page | 骨架项目详情页。 |
|
| `SkeletonProjectPage` | Page | 骨架项目详情页。 |
|
||||||
| `ArticleIndexPage` | Page | 文章列表。 |
|
| `ArticleIndexPage` | Page | 文章列表。 |
|
||||||
| `ArticlePage` | Page | 评测、对比、避坑指南等内容文章。 |
|
| `ArticlePage` | Page | 评测、对比、避坑指南等内容文章。 |
|
||||||
|
|
||||||
模型归属:`HomePage` 在 `home` app(`wagtail start` 生成,沿用不改名);其余 Page、Snippet 和文章模型集中在 `core` app(见本文第六节)。
|
模型归属:`HomePage` 在 `home` app(`wagtail start` 生成,沿用不改名);其余 Page、Snippet 和文章模型集中在 `core` app(见本文第六节)。
|
||||||
|
|
||||||
|
**页面树结构(唯一权威)**:
|
||||||
|
|
||||||
|
```text
|
||||||
|
HomePage (/)
|
||||||
|
├── ScenarioIndexPage (/scenarios/)
|
||||||
|
│ └── ScenarioPage (/scenarios/{slug}/)
|
||||||
|
├── ProjectIndexPage (/projects/)
|
||||||
|
│ └── SkeletonProjectPage (/projects/{slug}/)
|
||||||
|
└── ArticleIndexPage (/articles/)
|
||||||
|
└── ArticlePage (/articles/{slug}/)
|
||||||
|
```
|
||||||
|
|
||||||
|
- 项目详情页统一挂在 `ProjectIndexPage` 下,**不挂在场景页下**:一个项目属于多个场景(多对多),而页面树上只能有一个父节点。
|
||||||
|
- `ScenarioPage` 没有子项目页;它通过查询 `SkeletonProjectPage.objects.live().filter(scenarios=...)` 渲染本场景的项目列表。
|
||||||
|
- 列表筛选与分页在 `ProjectIndexPage.get_context()` 中做服务端查询实现(参数契约见 3.4),**不另建独立 Django view**。
|
||||||
|
- `/languages/{slug}/`、`/frameworks/{slug}/` MVP 由筛选参数替代,不建 Page(见 `routes.md`)。
|
||||||
|
|
||||||
|
**Wagtail 实现注意**:Page 模型上的多对多字段(`scenarios`、`languages`、`frameworks`、`databases`、`features`)必须用 modelcluster 的 `ParentalManyToManyField`,不能用 Django 普通 `ManyToManyField`,否则后台编辑、草稿和预览会出错。
|
||||||
|
|
||||||
### 3.2 Snippet / 辅助模型
|
### 3.2 Snippet / 辅助模型
|
||||||
|
|
||||||
| 模型 | 说明 |
|
| 模型 | 说明 |
|
||||||
|
|||||||
+4
-2
@@ -31,7 +31,7 @@
|
|||||||
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| T-101 | 建立场景、语言、框架、数据库、功能标签模型 | T-003 | Wagtail 后台可维护这些 Snippet;迁移文件生成并通过 migrate | TODO |
|
| 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-103 | 建立文章模型 | T-101 | 可维护文章列表和详情;文章可链接骨架项目 | TODO |
|
||||||
| T-104 | 准备首批种子内容 | T-102, T-103 | 至少有 3 个场景、5 个骨架项目、2 篇文章;候选项目、评分和评语须经人工确认,禁止模型编造(见详单) | TODO |
|
| T-104 | 准备首批种子内容 | T-102, T-103 | 至少有 3 个场景、5 个骨架项目、2 篇文章;候选项目、评分和评语须经人工确认,禁止模型编造(见详单) | TODO |
|
||||||
|
|
||||||
@@ -101,6 +101,8 @@
|
|||||||
### T-102 骨架项目详情模型
|
### T-102 骨架项目详情模型
|
||||||
|
|
||||||
- `SkeletonProjectPage`(`core` app):字段、必填/可空/默认严格按 `04-architecture.md` §3.3 表和附注实现,不增不减。
|
- `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)。
|
- 6 个评分字段带 `MinValueValidator(0)` / `MaxValueValidator(5)`;`total_score` 为 property(6 项之和,0-30)。
|
||||||
- 后台按 5 组分面板:基本信息 / 分类标签 / 评分 / 评测内容 / 展示控制。
|
- 后台按 5 组分面板:基本信息 / 分类标签 / 评分 / 评测内容 / 展示控制。
|
||||||
- 验证单元测试至少 3 个:合法对象 `full_clean()` 通过;某评分设为 6 时 `full_clean()` 抛 `ValidationError`;`total_score` 等于 6 项之和。`manage.py test` 全部通过。
|
- 验证单元测试至少 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-201 | `base.html` 含 `<meta name="viewport">`;纯 CSS 无构建链路;无超视口固定宽度容器 | `curl /` 返回 200;各页面模板均继承 `base.html` |
|
||||||
| T-202 | 首页三区块全部来自数据库查询(`is_featured`、最新文章),不硬编码内容 | 测试断言 featured 项目标题出现在 `/` 响应中 |
|
| T-202 | 首页三区块全部来自数据库查询(`is_featured`、最新文章),不硬编码内容 | 测试断言 featured 项目标题出现在 `/` 响应中 |
|
||||||
| T-203 | 场景页只列已发布(live)项目;分页每页 20;空场景渲染 `empty_state.html` | 测试两用例:有项目场景(200 且含项目名)、空场景(200 且含空状态文案) |
|
| 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-205 | 展示 §3.3 全部展示字段;`is_sponsored=True` 时渲染 `sponsored_badge.html` | 测试断言详情页含评分、推荐理由;赞助用例含 Sponsored 标识 |
|
||||||
| T-206 | 文章详情可点击进入关联项目详情页 | 测试断言文章页响应含项目详情链接 |
|
| T-206 | 文章详情可点击进入关联项目详情页 | 测试断言文章页响应含项目详情链接 |
|
||||||
| T-301 | title/description 用 Wagtail 自带 `seo_title` / `search_description`;sitemap 用 `wagtail.contrib.sitemaps` | `curl /sitemap.xml` 返回 200 且含项目 URL;首页响应含 `<meta name="description"` |
|
| T-301 | title/description 用 Wagtail 自带 `seo_title` / `search_description`;sitemap 用 `wagtail.contrib.sitemaps` | `curl /sitemap.xml` 返回 200 且含项目 URL;首页响应含 `<meta name="description"` |
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
### 项目列表页
|
### 项目列表页
|
||||||
|
|
||||||
|
- 由 `ProjectIndexPage` 承载;页面树位置与查询方式见 [`04-architecture.md`](04-architecture.md) §3.1。
|
||||||
- 展示所有已发布骨架项目。
|
- 展示所有已发布骨架项目。
|
||||||
- 支持按语言、框架、数据库、AI 分数、关键词筛选。
|
- 支持按语言、框架、数据库、AI 分数、关键词筛选。
|
||||||
- 筛选条件体现在 URL 查询参数中;参数名、取值和组合规则以 [`04-architecture.md`](04-architecture.md) §3.4 契约表为唯一权威,本文不重复维护。
|
- 筛选条件体现在 URL 查询参数中;参数名、取值和组合规则以 [`04-architecture.md`](04-architecture.md) §3.4 契约表为唯一权威,本文不重复维护。
|
||||||
|
|||||||
+12
@@ -80,3 +80,15 @@
|
|||||||
- 决策:筛选参数契约以 `04-architecture.md` §3.4 为唯一权威;`home`/`search` app 保留不改名;`AiCodingScore` 不建模型;种子内容评分必须人工确认。
|
- 决策:筛选参数契约以 `04-architecture.md` §3.4 为唯一权威;`home`/`search` app 保留不改名;`AiCodingScore` 不建模型;种子内容评分必须人工确认。
|
||||||
- 下一步:T-001 初始化 Wagtail 项目骨架(详单已可直接照做)。
|
- 下一步:T-001 初始化 Wagtail 项目骨架(详单已可直接照做)。
|
||||||
|
|
||||||
|
## 2026-07-06 补齐页面树决策与 Wagtail 多对多实现注意
|
||||||
|
|
||||||
|
- 状态:DONE
|
||||||
|
- 变更:
|
||||||
|
- `docs/04-architecture.md` §3.1:新增 `ProjectIndexPage`(`/projects/` 承载页);新增页面树结构图(唯一权威):项目详情统一挂 `ProjectIndexPage` 下、不挂场景页下(多对多场景 vs 单父节点树的冲突由此消解);`ScenarioPage` 用查询渲染项目、无子页面;筛选实现在 `ProjectIndexPage.get_context()`、不另建 Django view;新增 Wagtail 实现注意:Page 上的多对多必须用 modelcluster `ParentalManyToManyField`。
|
||||||
|
- `docs/06-tasks.md`:T-102 表行与详单同步上述两点(`ParentalManyToManyField`、同轮建 `ProjectIndexPage`);T-204 速查行标明筛选落点。
|
||||||
|
- `docs/routes.md`:项目列表页标注承载 Page 并指回 §3.1。
|
||||||
|
- 验证:`grep` 确认 `ProjectIndexPage` 在 04/06/routes 三处一致;`ParentalManyToManyField` 在 04 与 06 两处一致。
|
||||||
|
- 阻塞:无。
|
||||||
|
- 决策:页面树以 §3.1 结构图为唯一权威;`/languages/`、`/frameworks/` 聚合页 MVP 不建 Page,由筛选参数替代。
|
||||||
|
- 下一步:T-001 初始化 Wagtail 项目骨架。
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user