T-001: 初始化 Wagtail 项目骨架
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
# Django project
|
||||
/media/
|
||||
/static/
|
||||
*.sqlite3
|
||||
|
||||
# Python and others
|
||||
__pycache__
|
||||
*.pyc
|
||||
.DS_Store
|
||||
*.swp
|
||||
/venv/
|
||||
/tmp/
|
||||
/.vagrant/
|
||||
/Vagrantfile.local
|
||||
node_modules/
|
||||
/npm-debug.log
|
||||
/.idea/
|
||||
.vscode
|
||||
coverage
|
||||
.python-version
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class CoreConfig(AppConfig):
|
||||
name = 'core'
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
@@ -8,7 +8,7 @@
|
||||
| --- | --- | --- | --- |
|
||||
| CMS / Web 框架 | Wagtail + Django | 已定 | 内容模型、后台、页面、SEO、搜索和后续扩展都适合目录评测站。 |
|
||||
| Python | Python 3.12 | 已定 | Wagtail 当前支持 Python 3.12;部署环境更稳。 |
|
||||
| 版本钉死 | `requirements.txt` 按实际安装版本 pin | T-001 落实 | 初始化时安装 pip 当前稳定版 Wagtail,用 `pip show wagtail` 确认版本后写成 `wagtail==X.Y.Z`,并回写本表。 |
|
||||
| 版本钉死 | `requirements.txt` 按实际安装版本 pin | 已落实 | Wagtail 7.4.2 / Django 6.0.6,已 pin 全部依赖版本。 |
|
||||
| 数据库 | SQLite with JSON1 | 已定 | 开发和第一版上线成本低;MVP 写入少。 |
|
||||
| 后续数据库 | PostgreSQL | 条件触发 | 出现用户写入、高并发、后台多人编辑、锁冲突或会员功能后迁移。 |
|
||||
| 前端模板 | Django Templates / Wagtail Templates | 已定 | 第一版以内容站为主,避免引入前后端分离复杂度。 |
|
||||
@@ -17,7 +17,7 @@
|
||||
| 鉴权方式 | Wagtail Admin Session | 已定 | 第一版只有后台编辑账号。 |
|
||||
| 媒体存储 | 本地 media 目录 | 已定 | 2 核 2G VPS 第一版足够;后续可迁移 S3/R2/OSS。 |
|
||||
| 部署方式 | 单 VPS,Gunicorn + Nginx | 待实现 | 面向 2 核 2G VPS;先不引入 Docker 作为必需项。 |
|
||||
| 测试 | Django test / pytest 待定 | 待定 | T-001 初始化后以项目实际生成结构为准。 |
|
||||
| 测试 | Django test | 已定 | T-001 初始化后以各 app `tests.py` 为准。 |
|
||||
| 开发环境 | WSL2 / Linux + bash | 已定 | 仓库根目录 `/mnt/d/opc_project/skelet`;`init.sh` 为标准入口,`init.ps1` 可选。 |
|
||||
| 访问统计 | Plausible / Umami 或等价轻量方案 | 待实现 | 上线前接入(任务 T-305);自然搜索和外链点击是 M3/M4 商业验证的前置数据。 |
|
||||
|
||||
@@ -32,9 +32,7 @@
|
||||
|
||||
## 三、构建与运行命令
|
||||
|
||||
生产代码尚未初始化。完成 `T-001` 后必须把本节替换为真实命令。
|
||||
|
||||
目标命令形态:
|
||||
已完成初始化,以下为真实可用命令:
|
||||
|
||||
| 用途 | 命令 |
|
||||
| --- | --- |
|
||||
|
||||
@@ -216,9 +216,9 @@ skelet/
|
||||
└── README.md
|
||||
```
|
||||
|
||||
**结构决策**:保留 `wagtail start` 生成的 `home` 和 `search` app,不重命名(手改默认迁移容易出错);业务模型全部放在新建的 `core` app;测试放各 app 的 `tests.py`,暂不建独立 `tests/` 目录;生成的 `Dockerfile` 删除(第一版不用 Docker,见 `03-tech-stack.md`)。
|
||||
**结构决策**:保留 `wagtail start` 生成的 `home` 和 `search` app;业务模型全部放在新建的 `core` app;测试放各 app 的 `tests.py`,暂不建独立 `tests/` 目录;`Dockerfile` 已删除。
|
||||
|
||||
实际路径以初始化后的 Wagtail 项目为准;初始化完成后必须更新本文和 `current-state.md`。
|
||||
已初始化:Wagtail 7.4.2 + Django 6.0.6,SQLite 可用,superuser `admin` 已创建。
|
||||
|
||||
## 七、架构纪律
|
||||
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@
|
||||
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| T-000 | 初始化 harness 文档 | - | 根目录入口、需求、技术栈、架构、规则、任务和当前状态文档已建立 | DONE |
|
||||
| T-001 | 初始化 Wagtail 项目骨架 | T-000 | Python 3.12 虚拟环境可用;Wagtail/Django 项目生成;SQLite 配置可 migrate;首页或默认 Wagtail 页面可访问;`init.ps1` / `init.sh` 替换为真实命令 | TODO |
|
||||
| T-001 | 初始化 Wagtail 项目骨架 | T-000 | Python 3.12 虚拟环境可用;Wagtail/Django 项目生成;SQLite 配置可 migrate;首页或默认 Wagtail 页面可访问;`init.ps1` / `init.sh` 替换为真实命令 | DONE |
|
||||
| T-002 | 建立基础配置与环境样例 | T-001 | 存在 `requirements.txt`、`.env.example` 或等价配置说明;不包含真实密钥;`DEBUG`、`ALLOWED_HOSTS`、media/static 配置清楚 | TODO |
|
||||
| T-003 | 建立最小验证基线 | T-001 | `manage.py check` 和基础测试命令可运行;验证结果写入 `progress.md` | TODO |
|
||||
|
||||
|
||||
+19
-26
@@ -11,16 +11,16 @@
|
||||
## 当前快照
|
||||
|
||||
- 日期:2026-07-06
|
||||
- 阶段:MVP 起步前,harness 文档已初始化并完成瘦身(元文档合并为 `90-harness-reference.md`,入口统一为 `AGENTS.md`);任务看板已加固(任务详单、验证命令、筛选参数契约、字段必填/默认附注)
|
||||
- 开发环境:WSL2 / Linux,仓库根目录 `/mnt/d/opc_project/skelet`,bash 为标准命令形态;Python 命令统一使用 `python3.12`(系统已安装 3.12.12)
|
||||
- git:已初始化,主分支 `main`;`.gitignore` 已生效(`deploy/` 及凭证类文件不入库),`.gitattributes` 统一 LF 行尾
|
||||
- 技术栈:目标为 Wagtail + Django + Python 3.12 + SQLite;第一版英文单语言站点
|
||||
- 生产代码:尚未初始化
|
||||
- 测试:尚未初始化
|
||||
- 数据:尚未建立 seed 数据;未来通过 Wagtail 后台录入首批项目
|
||||
- 标准启动路径:`init.sh` 尚未配置真实命令(`init.ps1` 为可选辅助)
|
||||
- 标准验证路径:生产代码初始化后补齐
|
||||
- 当前 blocker:未初始化 Wagtail 项目骨架;下一步执行 `T-001`
|
||||
- 阶段:T-001 已完成,Wagtail 7.4.2 + Django 6.0.6 项目已初始化;生产代码可运行
|
||||
- 开发环境:MSYS2/MinGW Python 3.12.12,venv 使用 `--system-site-packages`(Pillow 由 MSYS2 预编译包提供)
|
||||
- git:分支 `ds`;`.gitignore` 已生效(`.venv/`、`db.sqlite3`、`media/` 等被忽略)
|
||||
- 技术栈:Wagtail 7.4.2 + Django 6.0.6 + Python 3.12 + SQLite;第一版英文单语言站点
|
||||
- 生产代码:已初始化,`manage.py`、`skelet/`、`home/`、`search/`、`core/` 已建
|
||||
- 测试:尚未初始化(T-003 待建)
|
||||
- 数据:已创建 superuser `admin`;尚未建立 seed 数据
|
||||
- 标准启动路径:`init.sh` 已配置 `pip install` / `manage.py check` / `manage.py runserver` 命令
|
||||
- 标准验证路径:`.venv/bin/python manage.py check` 通过(3 个 treebeard 兼容警告非阻塞)
|
||||
- 当前 blocker:无;下一步执行 `T-002`
|
||||
|
||||
## 当前目录要点
|
||||
|
||||
@@ -34,31 +34,24 @@
|
||||
| `deploy/` | 已有,不入库 | 本机部署凭证(SSH 私钥等),被 `.gitignore` 整目录忽略。 |
|
||||
| `.gitignore` / `.gitattributes` | 已有 | 忽略凭证与运行产物;统一 LF 行尾。 |
|
||||
| `init.ps1` / `init.sh` | 已有,占位 | T-001 初始化生产代码后替换真实命令。 |
|
||||
| `manage.py` | 待建 | Wagtail/Django 入口。 |
|
||||
| `requirements.txt` | 待建 | Python 依赖。 |
|
||||
| `skelet/` | 待建 | Django 配置包(`wagtail start` 生成 base/dev/production settings)。 |
|
||||
| `home/` / `search/` | 待建 | `wagtail start` 默认 app,保留不改名;`HomePage` 在 `home`。 |
|
||||
| `core/` | 待建 | 业务 app:Snippet、场景页、项目页、文章模型、模板和静态资源。 |
|
||||
| `manage.py` | 已有 | Wagtail/Django 入口。 |
|
||||
| `requirements.txt` | 已有 | Python 依赖,全部 pin 精确版本。 |
|
||||
| `skelet/` | 已有 | Django 配置包(`wagtail start` 生成 base/dev/production settings)。 |
|
||||
| `home/` / `search/` | 已有 | `wagtail start` 默认 app,保留不改名;`HomePage` 在 `home`。 |
|
||||
| `core/` | 已有 | 业务 app,已加入 `INSTALLED_APPS`;模型、模板、测试待建。 |
|
||||
| `.venv/` | 已有(不入库) | Python 3.12 虚拟环境,`--system-site-packages`(Pillow 12.0 来自 MSYS2)。 |
|
||||
| 各 app `tests.py` | 待建 | smoke 与模型测试(T-003 起),暂不建独立 `tests/` 目录。 |
|
||||
|
||||
## 任务看板状态
|
||||
|
||||
- 已完成:`T-000 初始化 harness 文档`。
|
||||
- 已完成:`T-000 初始化 harness 文档`、`T-001 初始化 Wagtail 项目骨架`。
|
||||
- 正在进行:无。
|
||||
- 下一个可领取任务:`T-001 初始化 Wagtail 项目骨架`。
|
||||
- 下一个可领取任务:`T-002 建立基础配置与环境样例`。
|
||||
|
||||
## 当前可运行内容
|
||||
|
||||
```bash
|
||||
# 当前只能做文档检查
|
||||
git status
|
||||
find . -type f -not -path "./.git/*"
|
||||
```
|
||||
|
||||
生产应用命令待 T-001 初始化后补齐:
|
||||
|
||||
```bash
|
||||
# 目标形态
|
||||
# 标准开发命令(在 MSYS2 bash 中运行)
|
||||
.venv/bin/python manage.py check
|
||||
.venv/bin/python manage.py test
|
||||
.venv/bin/python manage.py runserver
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class HomeConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "home"
|
||||
@@ -0,0 +1,31 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("wagtailcore", "0040_page_draft_title"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="HomePage",
|
||||
fields=[
|
||||
(
|
||||
"page_ptr",
|
||||
models.OneToOneField(
|
||||
on_delete=models.CASCADE,
|
||||
parent_link=True,
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="wagtailcore.Page",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
bases=("wagtailcore.page",),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,66 @@
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def create_homepage(apps, schema_editor):
|
||||
# Get models
|
||||
ContentType = apps.get_model("contenttypes.ContentType")
|
||||
Page = apps.get_model("wagtailcore.Page")
|
||||
Site = apps.get_model("wagtailcore.Site")
|
||||
HomePage = apps.get_model("home.HomePage")
|
||||
|
||||
# Delete the default homepage (of type Page) as created by wagtailcore.0002_initial_data,
|
||||
# if it exists
|
||||
page_content_type = ContentType.objects.get(
|
||||
model="page", app_label="wagtailcore"
|
||||
)
|
||||
Page.objects.filter(
|
||||
content_type=page_content_type, slug="home", depth=2
|
||||
).delete()
|
||||
|
||||
# Create content type for homepage model
|
||||
homepage_content_type, __ = ContentType.objects.get_or_create(
|
||||
model="homepage", app_label="home"
|
||||
)
|
||||
|
||||
# Create a new homepage
|
||||
homepage = HomePage.objects.create(
|
||||
title="Home",
|
||||
draft_title="Home",
|
||||
slug="home",
|
||||
content_type=homepage_content_type,
|
||||
path="00010001",
|
||||
depth=2,
|
||||
numchild=0,
|
||||
url_path="/home/",
|
||||
)
|
||||
|
||||
# Create a site with the new homepage set as the root
|
||||
Site.objects.create(hostname="localhost", root_page=homepage, is_default_site=True)
|
||||
|
||||
|
||||
def remove_homepage(apps, schema_editor):
|
||||
# Get models
|
||||
ContentType = apps.get_model("contenttypes.ContentType")
|
||||
HomePage = apps.get_model("home.HomePage")
|
||||
|
||||
# Delete the default homepage
|
||||
# Page and Site objects CASCADE
|
||||
HomePage.objects.filter(slug="home", depth=2).delete()
|
||||
|
||||
# Delete content type for homepage model
|
||||
ContentType.objects.filter(model="homepage", app_label="home").delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
run_before = [
|
||||
("wagtailcore", "0053_locale_model"),
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
("home", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(create_homepage, remove_homepage),
|
||||
]
|
||||
@@ -0,0 +1,7 @@
|
||||
from django.db import models
|
||||
|
||||
from wagtail.models import Page
|
||||
|
||||
|
||||
class HomePage(Page):
|
||||
pass
|
||||
@@ -0,0 +1,184 @@
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 960px;
|
||||
min-height: 100vh;
|
||||
margin: 0 auto;
|
||||
padding: 0 15px;
|
||||
color: #231f20;
|
||||
font-family: 'Helvetica Neue', 'Segoe UI', Arial, sans-serif;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
color: #308282;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #ea1b10;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
p,
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #e6e6e6;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 150px;
|
||||
margin-inline-end: 20px;
|
||||
}
|
||||
|
||||
.logo a {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.figure-logo {
|
||||
max-width: 150px;
|
||||
max-height: 55.1px;
|
||||
}
|
||||
|
||||
.release-notes {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.main {
|
||||
padding: 40px 0;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.figure-space {
|
||||
max-width: 265px;
|
||||
}
|
||||
|
||||
@keyframes pos {
|
||||
0%, 100% {
|
||||
transform: rotate(-6deg);
|
||||
}
|
||||
50% {
|
||||
transform: rotate(6deg);
|
||||
}
|
||||
}
|
||||
|
||||
.egg {
|
||||
fill: #43b1b0;
|
||||
animation: pos 3s ease infinite;
|
||||
transform: translateY(50px);
|
||||
transform-origin: 50% 80%;
|
||||
}
|
||||
|
||||
.main-text {
|
||||
max-width: 400px;
|
||||
margin: 5px auto;
|
||||
}
|
||||
|
||||
.main-text h1 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.main-text p {
|
||||
margin: 15px auto 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
border-top: 1px solid #e6e6e6;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.option {
|
||||
display: block;
|
||||
padding: 10px 10px 10px 34px;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.option svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
fill: gray;
|
||||
border: 1px solid #d9d9d9;
|
||||
padding: 5px;
|
||||
border-radius: 100%;
|
||||
top: 10px;
|
||||
inset-inline-start: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.option h2 {
|
||||
font-size: 19px;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.option p {
|
||||
padding-top: 3px;
|
||||
color: #231f20;
|
||||
font-size: 15px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
@media (max-width: 996px) {
|
||||
body {
|
||||
max-width: 780px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.option {
|
||||
flex: 0 0 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 599px) {
|
||||
.main {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.figure-space {
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: block;
|
||||
width: 300px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 360px) {
|
||||
.header-link {
|
||||
max-width: 100px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block body_class %}template-homepage{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
|
||||
{% comment %}
|
||||
Delete the line below if you're just getting started and want to remove the welcome screen!
|
||||
{% endcomment %}
|
||||
<link rel="stylesheet" href="{% static 'css/welcome_page.css' %}">
|
||||
{% endblock extra_css %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% comment %}
|
||||
Delete the line below if you're just getting started and want to remove the welcome screen!
|
||||
{% endcomment %}
|
||||
{% include 'home/welcome_page.html' %}
|
||||
|
||||
{% endblock content %}
|
||||
@@ -0,0 +1,52 @@
|
||||
{% load i18n wagtailcore_tags %}
|
||||
|
||||
<header class="header">
|
||||
<div class="logo">
|
||||
<a href="https://wagtail.org/">
|
||||
<svg class="figure-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 342.5 126.2"><title>{% trans "Visit the Wagtail website" %}</title><path fill="#FFF" d="M84 1.9v5.7s-10.2-3.8-16.8 3.1c-4.8 5-5.2 10.6-3 18.1 21.6 0 25 12.1 25 12.1L87 27l6.8-8.3c0-9.8-8.1-16.3-9.8-16.8z"/><circle cx="85.9" cy="15.9" r="2.6"/><path d="M89.2 40.9s-3.3-16.6-24.9-12.1c-2.2-7.5-1.8-13 3-18.1C73.8 3.8 84 7.6 84 7.6V1.9C80.4.3 77 0 73.2 0 59.3 0 51.6 10.4 48.3 17.4L9.2 89.3l11-2.1-20.2 39 14.1-2.5L24.9 93c30.6 0 69.8-11 64.3-52.1z"/><path d="M102.4 27l-8.6-8.3L87 27z"/><path fill="#FFF" d="M30 84.1s1-.2 2.8-.6c1.8-.4 4.3-1 7.3-1.8 1.5-.4 3.1-.9 4.8-1.5 1.7-.6 3.5-1.2 5.2-2 1.8-.7 3.6-1.6 5.4-2.6 1.8-1 3.5-2.1 5.1-3.4.4-.3.8-.6 1.2-1l1.2-1c.7-.7 1.5-1.4 2.2-2.2.7-.7 1.3-1.5 1.9-2.3l.9-1.2.4-.6.4-.6c.2-.4.5-.8.7-1.2.2-.4.4-.8.7-1.2l.3-.6.3-.6c.2-.4.4-.8.5-1.2l.9-2.4c.2-.8.5-1.6.7-2.3.2-.7.3-1.5.5-2.1.1-.7.2-1.3.3-2 .1-.6.2-1.2.2-1.7.1-.5.1-1 .2-1.5.1-1.8.1-2.8.1-2.8l1.6.1s-.1 1.1-.2 2.9c-.1.5-.1 1-.2 1.5-.1.6-.1 1.2-.3 1.8-.1.6-.3 1.3-.4 2-.2.7-.4 1.4-.6 2.2-.2.8-.5 1.5-.8 2.4-.3.8-.6 1.6-1 2.5l-.6 1.2-.3.6-.3.6c-.2.4-.5.8-.7 1.3-.3.4-.5.8-.8 1.2-.1.2-.3.4-.4.6l-.4.6-.9 1.2c-.7.8-1.3 1.6-2.1 2.3-.7.8-1.5 1.4-2.3 2.2l-1.2 1c-.4.3-.8.6-1.3.9-1.7 1.2-3.5 2.3-5.3 3.3-1.8.9-3.7 1.8-5.5 2.5-1.8.7-3.6 1.3-5.3 1.8-1.7.5-3.3 1-4.9 1.3-3 .7-5.6 1.3-7.4 1.6-1.6.6-2.6.8-2.6.8z"/><g fill="#231F20"><path d="M127 83.9h-8.8l-12.6-36.4h7.9l9 27.5 9-27.5h7.9l9 27.5 9-27.5h7.9L153 83.9h-8.8L135.6 59 127 83.9zM200.1 83.9h-7V79c-3 3.6-7 5.4-12.1 5.4-3.8 0-6.9-1.1-9.4-3.2s-3.7-5-3.7-8.6c0-3.6 1.3-6.3 4-8 2.6-1.8 6.2-2.7 10.7-2.7h9.9v-1.4c0-4.8-2.7-7.3-8.1-7.3-3.4 0-6.9 1.2-10.5 3.7l-3.4-4.8c4.4-3.5 9.4-5.3 15.1-5.3 4.3 0 7.8 1.1 10.5 3.2 2.7 2.2 4.1 5.6 4.1 10.2v23.7zm-7.7-13.6v-3.1h-8.6c-5.5 0-8.3 1.7-8.3 5.2 0 1.8.7 3.1 2.1 4.1 1.4.9 3.3 1.4 5.7 1.4 2.4 0 4.6-.7 6.4-2.1 1.8-1.3 2.7-3.1 2.7-5.5zM241.7 47.5v31.7c0 6.4-1.7 11.3-5.2 14.5-3.5 3.2-8 4.8-13.4 4.8-5.5 0-10.4-1.7-14.8-5.1l3.6-5.8c3.6 2.7 7.1 4 10.8 4 3.6 0 6.5-.9 8.6-2.8 2.1-1.9 3.2-4.9 3.2-9v-4.7c-1.1 2.1-2.8 3.9-4.9 5.1-2.1 1.3-4.5 1.9-7.1 1.9-4.8 0-8.8-1.7-11.9-5.1-3.1-3.4-4.7-7.6-4.7-12.6s1.6-9.2 4.7-12.6c3.1-3.4 7.1-5.1 11.9-5.1 4.8 0 8.7 2 11.7 6v-5.4h7.5zm-28.4 16.8c0 3 .9 5.6 2.8 7.7 1.8 2.2 4.3 3.2 7.5 3.2 3.1 0 5.7-1 7.6-3.1 1.9-2.1 2.9-4.7 2.9-7.8 0-3.1-1-5.8-2.9-7.9-2-2.2-4.5-3.2-7.6-3.2-3.1 0-5.6 1.1-7.4 3.4-2 2.1-2.9 4.7-2.9 7.7zM260.9 53.6v18.5c0 1.7.5 3.1 1.4 4.1.9 1 2.2 1.5 3.8 1.5 1.6 0 3.2-.8 4.7-2.4l3.1 5.4c-2.7 2.4-5.7 3.6-8.9 3.6-3.3 0-6-1.1-8.3-3.4-2.3-2.3-3.5-5.3-3.5-9.1V53.6h-4.6v-6.2h4.6V36.1h7.7v11.4h9.6v6.2h-9.6zM309.5 83.9h-7V79c-3 3.6-7 5.4-12.1 5.4-3.8 0-6.9-1.1-9.4-3.2s-3.7-5-3.7-8.6c0-3.6 1.3-6.3 4-8 2.6-1.8 6.2-2.7 10.7-2.7h9.9v-1.4c0-4.8-2.7-7.3-8.1-7.3-3.4 0-6.9 1.2-10.5 3.7l-3.4-4.8c4.4-3.5 9.4-5.3 15.1-5.3 4.3 0 7.8 1.1 10.5 3.2 2.7 2.2 4.1 5.6 4.1 10.2v23.7zm-7.7-13.6v-3.1h-8.6c-5.5 0-8.3 1.7-8.3 5.2 0 1.8.7 3.1 2.1 4.1 1.4.9 3.3 1.4 5.7 1.4 2.4 0 4.6-.7 6.4-2.1 1.8-1.3 2.7-3.1 2.7-5.5zM319.3 40.2c-1-1-1.4-2.1-1.4-3.4 0-1.3.5-2.5 1.4-3.4 1-1 2.1-1.4 3.4-1.4 1.3 0 2.5.5 3.4 1.4 1 1 1.4 2.1 1.4 3.4 0 1.3-.5 2.5-1.4 3.4s-2.1 1.4-3.4 1.4c-1.3.1-2.4-.4-3.4-1.4zm7.2 43.7h-7.7V47.5h7.7v36.4zM342.5 83.9h-7.7V33.1h7.7v50.8z"/></g></svg>
|
||||
</a>
|
||||
</div>
|
||||
<div class="header-link">
|
||||
{% comment %}
|
||||
This works for all cases but prerelease versions:
|
||||
{% endcomment %}
|
||||
<a href="{% wagtail_documentation_path %}/releases/{% wagtail_release_notes_path %}">
|
||||
{% trans "View the release notes" %}
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
<main class="main">
|
||||
<div class="figure">
|
||||
<svg class="figure-space" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300" aria-hidden="true">
|
||||
<path class="egg" fill="currentColor" d="M150 250c-42.741 0-75-32.693-75-90s42.913-110 75-110c32.088 0 75 52.693 75 110s-32.258 90-75 90z"/>
|
||||
<ellipse fill="#ddd" cx="150" cy="270" rx="40" ry="7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="main-text">
|
||||
<h1>{% trans "Welcome to your new Wagtail site!" %}</h1>
|
||||
<p>{% trans 'Please feel free to <a href="https://github.com/wagtail/wagtail/wiki/Slack">join our community on Slack</a>, or get started with one of the links below.' %}</p>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="footer" role="contentinfo">
|
||||
<a class="option option-one" href="{% wagtail_documentation_path %}/">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true"><path d="M9 21c0 .5.4 1 1 1h4c.6 0 1-.5 1-1v-1H9v1zm3-19C8.1 2 5 5.1 5 9c0 2.4 1.2 4.5 3 5.7V17c0 .5.4 1 1 1h6c.6 0 1-.5 1-1v-2.3c1.8-1.3 3-3.4 3-5.7 0-3.9-3.1-7-7-7zm2.9 11.1l-.9.6V16h-4v-2.3l-.9-.6C7.8 12.2 7 10.6 7 9c0-2.8 2.2-5 5-5s5 2.2 5 5c0 1.6-.8 3.2-2.1 4.1z"/></svg>
|
||||
<div>
|
||||
<h2>{% trans "Wagtail Documentation" %}</h2>
|
||||
<p>{% trans "Topics, references, & how-tos" %}</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="option option-two" href="{% wagtail_documentation_path %}/getting_started/tutorial.html">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></svg>
|
||||
<div>
|
||||
<h2>{% trans "Tutorial" %}</h2>
|
||||
<p>{% trans "Build your first Wagtail site" %}</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="option option-three" href="{% url 'wagtailadmin_home' %}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true"><path d="M0 0h24v24H0z" fill="none"/><path d="M16.5 13c-1.2 0-3.07.34-4.5 1-1.43-.67-3.3-1-4.5-1C5.33 13 1 14.08 1 16.25V19h22v-2.75c0-2.17-4.33-3.25-6.5-3.25zm-4 4.5h-10v-1.25c0-.54 2.56-1.75 5-1.75s5 1.21 5 1.75v1.25zm9 0H14v-1.25c0-.46-.2-.86-.52-1.22.88-.3 1.96-.53 3.02-.53 2.44 0 5 1.21 5 1.75v1.25zM7.5 12c1.93 0 3.5-1.57 3.5-3.5S9.43 5 7.5 5 4 6.57 4 8.5 5.57 12 7.5 12zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 5.5c1.93 0 3.5-1.57 3.5-3.5S18.43 5 16.5 5 13 6.57 13 8.5s1.57 3.5 3.5 3.5zm0-5.5c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2z"/></svg>
|
||||
<div>
|
||||
<h2>{% trans "Admin Interface" %}</h2>
|
||||
<p>{% trans "Create your superuser first!" %}</p>
|
||||
</div>
|
||||
</a>
|
||||
</footer>
|
||||
@@ -0,0 +1,42 @@
|
||||
from home.models import HomePage
|
||||
|
||||
from wagtail.models import Page, Site
|
||||
from wagtail.test.utils import WagtailPageTestCase
|
||||
|
||||
|
||||
class HomeSetUpTests(WagtailPageTestCase):
|
||||
"""
|
||||
Tests for basic page structure setup and HomePage creation.
|
||||
"""
|
||||
|
||||
def test_root_create(self):
|
||||
root_page = Page.objects.get(pk=1)
|
||||
self.assertIsNotNone(root_page)
|
||||
|
||||
def test_homepage_create(self):
|
||||
root_page = Page.objects.get(pk=1)
|
||||
homepage = HomePage(title="Home")
|
||||
root_page.add_child(instance=homepage)
|
||||
self.assertTrue(HomePage.objects.filter(title="Home").exists())
|
||||
|
||||
|
||||
class HomeTests(WagtailPageTestCase):
|
||||
"""
|
||||
Tests for homepage functionality and rendering.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Create a homepage instance for testing.
|
||||
"""
|
||||
root_page = Page.get_first_root_node()
|
||||
Site.objects.create(hostname="testsite", root_page=root_page, is_default_site=True)
|
||||
self.homepage = HomePage(title="Home")
|
||||
root_page.add_child(instance=self.homepage)
|
||||
|
||||
def test_homepage_is_renderable(self):
|
||||
self.assertPageIsRenderable(self.homepage)
|
||||
|
||||
def test_homepage_template_used(self):
|
||||
response = self.client.get(self.homepage.url)
|
||||
self.assertTemplateUsed(response, "home/home_page.html")
|
||||
@@ -4,32 +4,13 @@
|
||||
# - Windows 原生 PowerShell:用本文件 ./init.ps1
|
||||
# - WSL / Git Bash / macOS / Linux:用 ./init.sh
|
||||
# 一条命令完成:依赖安装 -> 基础验证 -> 打印启动命令。
|
||||
# 复制到新项目后,必须先替换下面三个命令,让每轮会话用同一条路径启动,不靠记忆。
|
||||
# 本文件不绑定任何技术栈;换技术栈时只替换这三个命令,脚本结构不用动。
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
Set-Location -Path $PSScriptRoot
|
||||
|
||||
# 按你的项目实际情况替换这三个命令。未替换前脚本会主动失败。
|
||||
$InstallCmd = "__REPLACE_INSTALL_CMD__" # 依赖安装,如 uv sync / poetry install / npm install
|
||||
$VerifyCmd = "__REPLACE_VERIFY_CMD__" # 基础验证 / smoke,如 python -m pytest / go test ./...
|
||||
$StartCmd = "__REPLACE_START_CMD__" # 开发启动,如 uvicorn app:app --reload / npm run dev
|
||||
|
||||
function Assert-Configured {
|
||||
param(
|
||||
[string]$Name,
|
||||
[string]$Value
|
||||
)
|
||||
|
||||
if ($Value -like "__REPLACE_*") {
|
||||
Write-Error "请先在 init.ps1 中替换 $Name。同步更新 docs/03-tech-stack.md、docs/00-ai-start-here.md 和 docs/current-state.md 中的命令。"
|
||||
exit 2
|
||||
}
|
||||
}
|
||||
|
||||
Assert-Configured -Name "InstallCmd" -Value $InstallCmd
|
||||
Assert-Configured -Name "VerifyCmd" -Value $VerifyCmd
|
||||
Assert-Configured -Name "StartCmd" -Value $StartCmd
|
||||
$InstallCmd = ".venv/bin/pip install -r requirements.txt"
|
||||
$VerifyCmd = ".venv/bin/python manage.py check"
|
||||
$StartCmd = ".venv/bin/python manage.py runserver"
|
||||
|
||||
Write-Host "==> 当前目录: $($PWD.Path)"
|
||||
|
||||
|
||||
@@ -4,32 +4,15 @@
|
||||
# - WSL / Git Bash / macOS / Linux:用本文件 ./init.sh
|
||||
# - Windows 原生 PowerShell:用 ./init.ps1
|
||||
# 一条命令完成:依赖安装 -> 基础验证 -> 打印启动命令。
|
||||
# 复制到新项目后,必须先替换下面三个变量,让每轮会话用同一条路径启动,不靠记忆。
|
||||
# 本文件不绑定任何技术栈;换技术栈时只替换这三个变量,脚本结构不用动。
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
# 按你的项目实际情况替换这三个命令。未替换前脚本会主动失败。
|
||||
INSTALL_CMD=(__REPLACE_INSTALL_CMD__) # 依赖安装,如 uv sync、poetry install、npm install
|
||||
VERIFY_CMD=(__REPLACE_VERIFY_CMD__) # 基础验证 / smoke,如 python -m pytest、go test ./...
|
||||
START_CMD=(__REPLACE_START_CMD__) # 开发启动,如 uvicorn app:app --reload、npm run dev
|
||||
|
||||
ensure_configured() {
|
||||
local name="$1"
|
||||
local first="$2"
|
||||
if [[ "$first" == __REPLACE_* ]]; then
|
||||
echo "ERROR: 请先在 init.sh 中替换 ${name}。"
|
||||
echo " 同步更新 docs/03-tech-stack.md、docs/00-ai-start-here.md 和 docs/current-state.md 中的命令。"
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_configured "INSTALL_CMD" "${INSTALL_CMD[0]}"
|
||||
ensure_configured "VERIFY_CMD" "${VERIFY_CMD[0]}"
|
||||
ensure_configured "START_CMD" "${START_CMD[0]}"
|
||||
INSTALL_CMD=(.venv/bin/pip install -r requirements.txt)
|
||||
VERIFY_CMD=(.venv/bin/python manage.py check)
|
||||
START_CMD=(.venv/bin/python manage.py runserver)
|
||||
|
||||
echo "==> 当前目录: $PWD"
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "skelet.settings.dev")
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
+23
@@ -101,3 +101,26 @@
|
||||
- 决策:开发统一使用 `python3.12` 命令(系统已安装 3.12.12),不依赖 `python3` 别名(指向 3.8.10)。
|
||||
- 下一步:T-001 初始化 Wagtail 项目骨架。
|
||||
|
||||
## 2026-07-06 T-001 初始化 Wagtail 项目骨架
|
||||
|
||||
- 状态:DONE
|
||||
- 变更:
|
||||
- 创建 Python 3.12 venv(`--system-site-packages`,Pillow 由 MSYS2 `mingw-w64-x86_64-python-pillow` 提供)
|
||||
- 安装 Wagtail 7.4.2 + Django 6.0.6 + 全部依赖(通过 MSYS2 bash 环境)
|
||||
- `wagtail start skelet .` 生成项目骨架(`manage.py`、`skelet/`、`home/`、`search/`)
|
||||
- `manage.py startapp core` 并加入 `INSTALLED_APPS`
|
||||
- 删除 `Dockerfile`;`requirements.txt` 替换为 `pip freeze` 精确版本
|
||||
- `manage.py migrate` 成功;`createsuperuser`(admin/admin123)
|
||||
- `init.sh` / `init.ps1` 替换为真实命令(`pip install`、`manage.py check`、`manage.py runserver`)
|
||||
- 回写 `03-tech-stack.md`(版本钉死状态、测试方案、§三命令)、`04-architecture.md` §六(结构决策、已初始化标记)、`current-state.md`(快照、目录表、任务看板状态、可运行命令)、`06-tasks.md`(T-001→DONE)
|
||||
- 验证:
|
||||
- `manage.py check`:0 errors, 3 treebeard 兼容警告(非阻塞)
|
||||
- `curl http://127.0.0.1:8000/`:HTTP 200
|
||||
- `curl http://127.0.0.1:8000/admin/login/`:HTTP 200
|
||||
- 阻塞:无。
|
||||
- 决策:
|
||||
- Python 3.12 来自 MSYS2/MinGW(`C:\msys64\mingw64\bin\python3.12.exe`),创建 Unix 风格 venv(`bin/` 非 `Scripts/`)
|
||||
- 因 MinGW 工具链版本冲突(GCC 8.1 vs Python 3.12),采用 `--system-site-packages` 复用 MSYS2 预编译 Pillow 12.0,非纯 Python 依赖(pillow-heif)在 MSYS2 bash 中编译
|
||||
- 开发命令需在 MSYS2 bash shell 中运行;`init.ps1` 在 PowerShell 中直接调用 `.venv/bin/` 路径可工作
|
||||
- 下一步:T-002 建立基础配置与环境样例。
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Django==6.0.6
|
||||
wagtail==7.4.2
|
||||
anyascii==0.3.3
|
||||
asgiref==3.11.1
|
||||
beautifulsoup4==4.15.0
|
||||
certifi==2026.6.17
|
||||
charset-normalizer==3.4.7
|
||||
defusedxml==0.7.1
|
||||
django-filter==25.2
|
||||
django-modelcluster==6.5
|
||||
django-permissionedforms==0.1
|
||||
django-stubs-ext==6.0.6
|
||||
django-taggit==6.1.0
|
||||
django-tasks==0.12.0
|
||||
django-treebeard==5.3.0
|
||||
djangorestframework==3.17.1
|
||||
draftjs_exporter==5.2.0
|
||||
et_xmlfile==2.0.0
|
||||
filetype==1.2.0
|
||||
idna==3.18
|
||||
laces==0.1.2
|
||||
modelsearch==1.3.1
|
||||
openpyxl==3.1.5
|
||||
packaging==25.0
|
||||
pillow==12.0.0
|
||||
pillow_heif==1.4.0
|
||||
requests==2.34.2
|
||||
soupsieve==2.8.4
|
||||
sqlparse==0.5.5
|
||||
telepath==0.3.1
|
||||
typing_extensions==4.16.0
|
||||
tzdata==2026.2
|
||||
urllib3==2.7.0
|
||||
Willow==1.12.0
|
||||
@@ -0,0 +1,38 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static wagtailcore_tags %}
|
||||
|
||||
{% block body_class %}template-searchresults{% endblock %}
|
||||
|
||||
{% block title %}Search{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Search</h1>
|
||||
|
||||
<form action="{% url 'search' %}" method="get">
|
||||
<input type="text" name="query"{% if search_query %} value="{{ search_query }}"{% endif %}>
|
||||
<input type="submit" value="Search" class="button">
|
||||
</form>
|
||||
|
||||
{% if search_results %}
|
||||
<ul>
|
||||
{% for result in search_results %}
|
||||
<li>
|
||||
<h4><a href="{% pageurl result %}">{{ result }}</a></h4>
|
||||
{% if result.search_description %}
|
||||
{{ result.search_description }}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{% if search_results.has_previous %}
|
||||
<a href="{% url 'search' %}?query={{ search_query|urlencode }}&page={{ search_results.previous_page_number }}">Previous</a>
|
||||
{% endif %}
|
||||
|
||||
{% if search_results.has_next %}
|
||||
<a href="{% url 'search' %}?query={{ search_query|urlencode }}&page={{ search_results.next_page_number }}">Next</a>
|
||||
{% endif %}
|
||||
{% elif search_query %}
|
||||
No results found
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,46 @@
|
||||
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
|
||||
from django.template.response import TemplateResponse
|
||||
|
||||
from wagtail.models import Page
|
||||
|
||||
# To enable logging of search queries for use with the "Promoted search results" module
|
||||
# <https://docs.wagtail.org/en/stable/reference/contrib/searchpromotions.html>
|
||||
# uncomment the following line and the lines indicated in the search function
|
||||
# (after adding wagtail.contrib.search_promotions to INSTALLED_APPS):
|
||||
|
||||
# from wagtail.contrib.search_promotions.models import Query
|
||||
|
||||
|
||||
def search(request):
|
||||
search_query = request.GET.get("query", None)
|
||||
page = request.GET.get("page", 1)
|
||||
|
||||
# Search
|
||||
if search_query:
|
||||
search_results = Page.objects.live().search(search_query)
|
||||
|
||||
# To log this query for use with the "Promoted search results" module:
|
||||
|
||||
# query = Query.get(search_query)
|
||||
# query.add_hit()
|
||||
|
||||
else:
|
||||
search_results = Page.objects.none()
|
||||
|
||||
# Pagination
|
||||
paginator = Paginator(search_results, 10)
|
||||
try:
|
||||
search_results = paginator.page(page)
|
||||
except PageNotAnInteger:
|
||||
search_results = paginator.page(1)
|
||||
except EmptyPage:
|
||||
search_results = paginator.page(paginator.num_pages)
|
||||
|
||||
return TemplateResponse(
|
||||
request,
|
||||
"search/search.html",
|
||||
{
|
||||
"search_query": search_query,
|
||||
"search_results": search_results,
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,185 @@
|
||||
"""
|
||||
Django settings for skelet project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 6.0.6.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/6.0/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/6.0/ref/settings/
|
||||
"""
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
from pathlib import Path
|
||||
|
||||
PROJECT_DIR = Path(__file__).resolve().parent.parent
|
||||
BASE_DIR = PROJECT_DIR.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
"core",
|
||||
"home",
|
||||
"search",
|
||||
"wagtail.contrib.forms",
|
||||
"wagtail.contrib.redirects",
|
||||
"wagtail.embeds",
|
||||
"wagtail.sites",
|
||||
"wagtail.users",
|
||||
"wagtail.snippets",
|
||||
"wagtail.documents",
|
||||
"wagtail.images",
|
||||
"wagtail.search",
|
||||
"wagtail.admin",
|
||||
"wagtail",
|
||||
"modelcluster",
|
||||
"taggit",
|
||||
"django_filters",
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"wagtail.contrib.redirects.middleware.RedirectMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = "skelet.urls"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [
|
||||
PROJECT_DIR / "templates",
|
||||
],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = "skelet.wsgi.application"
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/6.0/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": BASE_DIR / "db.sqlite3",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/6.0/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/6.0/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = "en-us"
|
||||
|
||||
TIME_ZONE = "UTC"
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/6.0/howto/static-files/
|
||||
|
||||
STATICFILES_FINDERS = [
|
||||
"django.contrib.staticfiles.finders.FileSystemFinder",
|
||||
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
||||
]
|
||||
|
||||
STATICFILES_DIRS = [
|
||||
PROJECT_DIR / "static",
|
||||
]
|
||||
|
||||
STATIC_ROOT = BASE_DIR / "static"
|
||||
STATIC_URL = "/static/"
|
||||
|
||||
MEDIA_ROOT = BASE_DIR / "media"
|
||||
MEDIA_URL = "/media/"
|
||||
|
||||
# Default storage settings
|
||||
# See https://docs.djangoproject.com/en/6.0/ref/settings/#std-setting-STORAGES
|
||||
STORAGES = {
|
||||
"default": {
|
||||
"BACKEND": "django.core.files.storage.FileSystemStorage",
|
||||
},
|
||||
"staticfiles": {
|
||||
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
|
||||
},
|
||||
}
|
||||
|
||||
# Django sets a maximum of 1000 fields per form by default, but particularly complex page models
|
||||
# can exceed this limit within Wagtail's page editor.
|
||||
DATA_UPLOAD_MAX_NUMBER_FIELDS = 10_000
|
||||
|
||||
|
||||
# Wagtail settings
|
||||
|
||||
WAGTAIL_SITE_NAME = "skelet"
|
||||
|
||||
# Search
|
||||
# https://docs.wagtail.org/en/stable/topics/search/backends.html
|
||||
WAGTAILSEARCH_BACKENDS = {
|
||||
"default": {
|
||||
"BACKEND": "wagtail.search.backends.database",
|
||||
}
|
||||
}
|
||||
|
||||
# Base URL to use when referring to full URLs within the Wagtail admin backend -
|
||||
# e.g. in notification emails. Don't include '/admin' or a trailing slash
|
||||
WAGTAILADMIN_BASE_URL = "http://example.com"
|
||||
|
||||
# Allowed file extensions for documents in the document library.
|
||||
# This can be omitted to allow all files, but note that this may present a security risk
|
||||
# if untrusted users are allowed to upload files -
|
||||
# see https://docs.wagtail.org/en/stable/advanced_topics/deploying.html#user-uploaded-files
|
||||
WAGTAILDOCS_EXTENSIONS = ['csv', 'docx', 'key', 'odt', 'pdf', 'pptx', 'rtf', 'txt', 'xlsx', 'zip']
|
||||
|
||||
# Maximum upload size for documents in bytes.
|
||||
WAGTAILDOCS_MAX_UPLOAD_SIZE = 10 * 1024 * 1024 # 10MB
|
||||
@@ -0,0 +1,18 @@
|
||||
from .base import *
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = "django-insecure-a5vf@_(tk2i0e*srydb+d3mnzz1)h23%+z5^53+gq8p#0vz+x-"
|
||||
|
||||
# SECURITY WARNING: define the correct hosts in production!
|
||||
ALLOWED_HOSTS = ["*"]
|
||||
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
|
||||
|
||||
try:
|
||||
from .local import *
|
||||
except ImportError:
|
||||
pass
|
||||
@@ -0,0 +1,14 @@
|
||||
from .base import *
|
||||
|
||||
DEBUG = False
|
||||
|
||||
# ManifestStaticFilesStorage is recommended in production, to prevent
|
||||
# outdated JavaScript / CSS assets being served from cache
|
||||
# (e.g. after a Wagtail upgrade).
|
||||
# See https://docs.djangoproject.com/en/6.0/ref/contrib/staticfiles/#manifeststaticfilesstorage
|
||||
STORAGES["staticfiles"]["BACKEND"] = "django.contrib.staticfiles.storage.ManifestStaticFilesStorage"
|
||||
|
||||
try:
|
||||
from .local import *
|
||||
except ImportError:
|
||||
pass
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Page not found{% endblock %}
|
||||
|
||||
{% block body_class %}template-404{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Page not found</h1>
|
||||
|
||||
<h2>Sorry, this page could not be found.</h2>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Internal server error</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>Internal server error</h1>
|
||||
|
||||
<h2>Sorry, there seems to be an error. Please try again soon.</h2>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,46 @@
|
||||
{% load static wagtailcore_tags wagtailuserbar %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>
|
||||
{% block title %}
|
||||
{% if page.seo_title %}{{ page.seo_title }}{% else %}{{ page.title }}{% endif %}
|
||||
{% endblock %}
|
||||
{% block title_suffix %}
|
||||
{% wagtail_site as current_site %}
|
||||
{% if current_site and current_site.site_name %}- {{ current_site.site_name }}{% endif %}
|
||||
{% endblock %}
|
||||
</title>
|
||||
{% if page.search_description %}
|
||||
<meta name="description" content="{{ page.search_description }}" />
|
||||
{% endif %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
|
||||
{# Force all links in the live preview panel to be opened in a new tab #}
|
||||
{% if request.in_preview_panel %}
|
||||
<base target="_blank">
|
||||
{% endif %}
|
||||
|
||||
{# Global stylesheets #}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/skelet.css' %}">
|
||||
|
||||
{% block extra_css %}
|
||||
{# Override this in templates to add extra stylesheets #}
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
||||
<body class="{% block body_class %}{% endblock %}">
|
||||
{% wagtailuserbar %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
|
||||
{# Global javascript #}
|
||||
<script type="text/javascript" src="{% static 'js/skelet.js' %}"></script>
|
||||
|
||||
{% block extra_js %}
|
||||
{# Override this in templates to add extra javascript #}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,35 @@
|
||||
from django.conf import settings
|
||||
from django.urls import include, path
|
||||
from django.contrib import admin
|
||||
|
||||
from wagtail.admin import urls as wagtailadmin_urls
|
||||
from wagtail import urls as wagtail_urls
|
||||
from wagtail.documents import urls as wagtaildocs_urls
|
||||
|
||||
from search import views as search_views
|
||||
|
||||
urlpatterns = [
|
||||
path("django-admin/", admin.site.urls),
|
||||
path("admin/", include(wagtailadmin_urls)),
|
||||
path("documents/", include(wagtaildocs_urls)),
|
||||
path("search/", search_views.search, name="search"),
|
||||
]
|
||||
|
||||
|
||||
if settings.DEBUG:
|
||||
from django.conf.urls.static import static
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
|
||||
# Serve static and media files from development server
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
urlpatterns = urlpatterns + [
|
||||
# For anything not caught by a more specific rule above, hand over to
|
||||
# Wagtail's page serving mechanism. This should be the last pattern in
|
||||
# the list:
|
||||
path("", include(wagtail_urls)),
|
||||
# Alternatively, if you want Wagtail pages to be served from a subpath
|
||||
# of your site, rather than the site root:
|
||||
# path("pages/", include(wagtail_urls)),
|
||||
]
|
||||
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
WSGI config for skelet project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/6.0/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "skelet.settings.dev")
|
||||
|
||||
application = get_wsgi_application()
|
||||
Reference in New Issue
Block a user