django-wiki 0.13.0 配置参数详解

django-wiki 0.13.0 配置参数体系详解:WIKI_ 前缀覆写机制、39 项生效参数实测总览、渲染净化/权限/限流/存储/通知分类详解与生产配置模板

django-wikiPythonDjangoWiki知识库2026/11/01

django-wiki 0.13.0 配置参数详解

目标:系统讲清 django-wiki 0.13.0 的配置参数体系——WIKI_ 前缀覆写机制、全部参数的生效值与默认值、核心参数(编辑器与渲染净化、权限账户、反滥用限流、附件图片存储、缓存、通知子系统)的作用与实测效果,并给出生产配置模板。全部内容已在 Debian 13 + Python 3.12.16 + django-wiki 0.13.0 + Django 6.0.9 环境实测验证。

一、环境信息

  • 操作系统:Debian 13(trixie)
  • Python:3.12.16(虚拟环境 /srv/wiki/venv)
  • django-wiki:0.13.0(PyPI 包名 wiki)
  • Django:6.0.9
  • 工程目录:/srv/wiki,settings 模块 wikisite.settings(文件 /srv/wiki/wikisite/settings.py)
  • 服务托管:gunicorn 26.2.0,监听 0.0.0.0:8000
  • 数据库:SQLite(/srv/wiki/db.sqlite3)
  • 演示账号:超级用户 root,密码 q1w2e3r4

💡 说明:本篇为配置专题篇,不重复安装过程。组件尚未部署的,请先按《Debian 13 安装 django-wiki 0.13.0 指南》完成安装与工程集成。

二、前置条件

  1. django-wiki 0.13.0 已安装并完成 migrate,工程可正常运行:
cd /srv/wiki
./venv/bin/python manage.py check
System check identified no issues (0 silenced).
  1. 已启用 django-wiki 核心应用与通知插件(INSTALLED_APPS 含 wiki.apps.WikiConfig 与 wiki.plugins.notifications.apps.NotificationsConfig 等,完整清单见安装篇)。
  2. 后续所有命令均在工程目录 /srv/wiki 下以 ./venv/bin/python manage.py ... 形式执行。

三、参数查看与修改方式

3.1 配置机制:WIKI_ 前缀覆写

django-wiki 没有独立的配置文件。它作为 Django 应用被嵌入工程,全部参数由库内模块 wiki/conf/settings.py 统一读取:每个参数都用 getattr(django_settings, "WIKI_XXX", 默认值) 的形式定义——在工程 settings.py 里写 WIKI_XXX 即覆写默认值,不写就用默认值。源码典型片段(实测环境 /srv/wiki/venv/lib/python3.12/site-packages/wiki/conf/settings.py,逐行照抄):

#: Should urls be case sensitive?
URL_CASE_SENSITIVE = getattr(django_settings, "WIKI_URL_CASE_SENSITIVE", False)

#: The editor class to use -- maybe a 3rd party or your own...? You can always
#: extend the built-in editor and customize it!
EDITOR = getattr(
    django_settings, "WIKI_EDITOR", "wiki.editors.markitup.MarkItUp"
)

#: Whether to use Bleach or not. It's not recommended to turn this off unless
#: you know what you're doing and you want to use the other options.
MARKDOWN_SANITIZE_HTML = getattr(
    django_settings, "WIKI_MARKDOWN_SANITIZE_HTML", True
)

注意三点:

  1. 项目侧键名一律带 WIKI_ 前缀,库内生效名不带前缀(如项目写 WIKI_EDITOR,库内读作 EDITOR)。
  2. 插件参数有各自的子前缀:附件插件用 WIKI_ATTACHMENTS_*、图片插件用 WIKI_IMAGES_*、分节编辑用 WIKI_EDITSECTION_*、链接插件用 WIKI_LINKS_*、宏插件用 WIKI_PLUGINS_METHODS。
  3. 通知子系统是独立的 django_nyt 包,其参数用 NYT_ 前缀,直接写在工程 settings.py(见 5.7 节)。

3.2 查看生效值

cd /srv/wiki
./venv/bin/python manage.py shell -c 'from wiki.conf import settings as w; print("CACHE_TIMEOUT =", w.CACHE_TIMEOUT); print("REVISIONS_PER_MINUTES =", w.REVISIONS_PER_MINUTES)'

实测回显:

32 objects imported automatically (use -v 2 for details).

CACHE_TIMEOUT = 600
REVISIONS_PER_MINUTES = 5

要看全部生效值,可遍历模块的大写属性:

./venv/bin/python manage.py shell -c '
from wiki.conf import settings as w
for k in sorted(x for x in dir(w) if x.isupper()):
    print(k, "=", repr(getattr(w, k)))
'

实测输出(39 项,即「四、参数总览」表的来源):

ACCOUNT_HANDLING = True
ACCOUNT_SIGNUP_ALLOWED = True
ANONYMOUS = True
ANONYMOUS_CREATE = False
ANONYMOUS_UPLOAD = False
ANONYMOUS_WRITE = False
CACHE_TIMEOUT = 600
CAN_ADMIN = None
CAN_ASSIGN = None
CAN_ASSIGN_OWNER = None
CAN_CHANGE_PERMISSIONS = None
CAN_DELETE = None
CAN_MODERATE = None
CAN_READ = None
CAN_WRITE = None
CHECK_SLUG_URL_AVAILABLE = True
EDITOR = 'wiki.editors.markitup.MarkItUp'
GROUP_MODEL = 'auth.Group'
LOG_IPS_ANONYMOUS = True
LOG_IPS_USERS = False
LOST_AND_FOUND_SLUG = 'lost-and-found'
MARKDOWN_HTML_ATTRIBUTES = {'a': ['href', 'title', 'class', 'id', 'target', 'rel'], 'abbr': ['title', 'class', 'id', 'target', 'rel'], 'acronym': [ ...
MARKDOWN_HTML_STYLES = []
MARKDOWN_HTML_WHITELIST = frozenset({'tr', 'li', 'br', 'code', 'b', 'hr', 'pre', 'abbr', 'figcaption', 'dt', 'thead', 'acronym', 'h5', 'h3', 'dl', ...
MARKDOWN_KWARGS = {'extensions': ['markdown.extensions.footnotes', 'markdown.extensions.attr_list', 'markdown.extensions.footnotes', 'mark ...
MARKDOWN_SANITIZE_HTML = True
MESSAGE_TAG_CSS_CLASS = {10: 'alert alert-info', 40: 'alert alert-danger', 20: 'alert alert-info', 25: 'alert alert-success', 30: 'alert alert-w ...
REVISIONS_MINUTES_LOOKBACK = 2
REVISIONS_PER_HOUR = 60
REVISIONS_PER_HOUR_ANONYMOUS = 10
REVISIONS_PER_MINUTES = 5
REVISIONS_PER_MINUTES_ANONYMOUS = 2
SHOW_MAX_CHILDREN = 20
STORAGE_BACKEND = <django.core.files.storage.DefaultStorage object at 0x7c04095fafc0>
URL_CASE_SENSITIVE = False
URL_CONFIG_CLASS = None
USE_BOOTSTRAP_SELECT_WIDGET = True
USE_SENDFILE = False
WIKILINKS_TRAILING_SLASH = True
WIKI_LANGUAGE = 'markdown'

(行尾带 ... 的是 Django shell 对超长值的截断显示,完整内容见 5.2 节实测。)

3.3 用 diffsettings 查看项目已覆写的参数

./venv/bin/python manage.py diffsettings | grep WIKI_

实测回显(当前工程 settings.py 显式定义的只有两项):

WIKI_ACCOUNT_HANDLING = True  ###
WIKI_ACCOUNT_SIGNUP_ALLOWED = True  ###

### 标记表示该项与 Django 默认值不同、由项目显式定义,是快速核对「哪些配置真正生效」的可靠手段。

3.4 修改与生效(实测)

向工程 settings.py 追加一行覆写:

cd /srv/wiki
echo 'WIKI_CACHE_TIMEOUT = 300' >> wikisite/settings.py

立即验证生效值(manage.py shell 每次都是新进程,直接读取修改后的 settings):

./venv/bin/python manage.py shell -c 'from wiki.conf import settings as w; print("CACHE_TIMEOUT =", w.CACHE_TIMEOUT)'
32 objects imported automatically (use -v 2 for details).

CACHE_TIMEOUT = 300

diffsettings 同步出现该项:

WIKI_ACCOUNT_HANDLING = True  ###
WIKI_ACCOUNT_SIGNUP_ALLOWED = True  ###
WIKI_CACHE_TIMEOUT = 300  ###

删除该行后恢复默认:

sed -i '/^WIKI_CACHE_TIMEOUT = 300$/d' wikisite/settings.py
./venv/bin/python manage.py shell -c 'from wiki.conf import settings as w; print("CACHE_TIMEOUT =", w.CACHE_TIMEOUT)'
32 objects imported automatically (use -v 2 for details).

CACHE_TIMEOUT = 600

⚠️ 注意:settings.py 在 Web 进程启动时加载一次。修改后必须重启 gunicorn(或 uwsgi 等)才对线上页面生效;改完先用 manage.py shell 验证,再重启服务,是推荐流程。

3.5 编辑器选项说明

💡 技术拓展:django-wiki 内置编辑器只有 MarkItUp 一种(实测 wiki/editors/ 目录仅含 base.py 与 markitup.py)。WIKI_EDITOR 可指向自定义类(继承 wiki.editors.base.TextareaBase 即可),社区另有 CKEditor 等第三方集成;三者对比:MarkItUp 零依赖开箱即用、纯 textarea+Markdown 预览;自定义 TextareaBase 适合接入现代前端编辑器(如 CodeMirror);CKEditor 富文本体验最好但需额外维护静态资源与净化白名单。

四、参数总览

下表为全部核心参数的「项目侧键名 / 默认生效值 / 作用」总览(生效值均来自 3.2 节实测输出):

分类项目侧键名默认值作用
URL 路由WIKI_URL_CASE_SENSITIVEFalse文章 URL 是否区分大小写
URL 路由WIKI_CHECK_SLUG_URL_AVAILABLETrue阻止与路由表冲突的新建 slug
URL 路由WIKI_LOST_AND_FOUND_SLUG'lost-and-found'父文章被删后子文章的收容 slug
URL 路由WIKI_URL_CONFIG_CLASSNone自定义 URL 模式类(默认 wiki.urls.WikiURLPatterns)
URL 路由WIKI_WIKILINKS_TRAILING_SLASHTrue渲染 [[链接]] 是否带尾部斜杠
编辑器渲染WIKI_EDITOR'wiki.editors.markitup.MarkItUp'编辑器类
编辑器渲染WIKI_MARKDOWN_SANITIZE_HTMLTrue是否用 bleach 净化输出 HTML
编辑器渲染WIKI_MARKDOWN_HTML_WHITELIST37 个标签(见 5.2)允许的 HTML 标签集合
编辑器渲染WIKI_MARKDOWN_HTML_ATTRIBUTES自动构建的 dict各标签允许的属性
编辑器渲染WIKI_MARKDOWN_HTML_STYLES[]允许的行内 style(默认全禁)
编辑器渲染WIKI_MARKDOWN_KWARGS见 5.2Python-Markdown 扩展与参数
权限账户WIKI_CAN_READ / WIKI_CAN_WRITE / WIKI_CAN_ASSIGN / WIKI_CAN_ASSIGN_OWNER / WIKI_CAN_CHANGE_PERMISSIONS / WIKI_CAN_DELETE / WIKI_CAN_MODERATE / WIKI_CAN_ADMIN全部 None各类权限的全局接管函数
权限账户WIKI_ANONYMOUSTrue匿名用户按 "other" 组对待
权限账户WIKI_ANONYMOUS_WRITEFalse匿名可编辑
权限账户WIKI_ANONYMOUS_CREATEFalse(跟随 WRITE)匿名可建文章
权限账户WIKI_ANONYMOUS_UPLOADFalse匿名可上传(附件/图片插件默认继承)
权限账户WIKI_ACCOUNT_HANDLINGTrue提供注册/登录/注销页面
权限账户WIKI_ACCOUNT_SIGNUP_ALLOWEDTrue允许自助注册
权限账户WIKI_GROUP_MODEL'auth.Group'权限使用的 Group 模型
反滥用WIKI_REVISIONS_PER_HOUR60登录用户每小时最大修订数
反滥用WIKI_REVISIONS_PER_MINUTES5登录用户每分钟最大修订数
反滥用WIKI_REVISIONS_PER_HOUR_ANONYMOUS10匿名每小时最大修订数
反滥用WIKI_REVISIONS_PER_MINUTES_ANONYMOUS2匿名每分钟最大修订数
反滥用WIKI_REVISIONS_MINUTES_LOOKBACK2分钟级限流的回看窗口(分钟)
存储WIKI_STORAGE_BACKENDDjango default_storage图片/附件存储后端
存储WIKI_ATTACHMENTS_USE_SENDFILEFalse是否用 django-sendfile 发送文件
缓存杂项WIKI_CACHE_TIMEOUT600文章缓存超时(秒)
缓存杂项WIKI_SHOW_MAX_CHILDREN20子文章菜单显示上限(禁设 0)
缓存杂项WIKI_USE_BOOTSTRAP_SELECT_WIDGETTrueBootstrap 下拉组件开关
缓存杂项WIKI_MESSAGE_TAG_CSS_CLASSalert 映射 dict消息级别到 CSS 类映射
缓存杂项WIKI_LOG_IPS_ANONYMOUSTrue记录匿名用户 IP
缓存杂项WIKI_LOG_IPS_USERSFalse记录登录用户 IP

五、核心参数分类详解

5.1 URL 与路由

  • WIKI_URL_CASE_SENSITIVE(默认 False):文章 slug 匹配不区分大小写。开启后 Page 与 page 是两篇不同文章,官方源码注释提醒 "Should urls be case sensitive?",一般保持默认。
  • WIKI_CHECK_SLUG_URL_AVAILABLE(默认 True):新建文章时校验 slug 解析结果是否落在 wiki 视图内,防止用户创建 /admin 之类与其他应用重叠的 URL。关掉它可提升新建速度(每次新建都要做一次 URL 解析),但需自行保证 slug 不与路由表冲突。
  • WIKI_LOST_AND_FOUND_SLUG(默认 'lost-and-found'):删除某 URLPath 文章后,其子文章连同权限与内容整体迁移到该 slug 下,避免孤儿子树丢失。
  • WIKI_URL_CONFIG_CLASS(默认 None):指向 URL 模式构造类,默认 wiki.urls.WikiURLPatterns;需要定制 wiki 路由或视图处理器时继承该类覆写。
  • WIKI_WIKILINKS_TRAILING_SLASH(默认 True):正文里 [[某文章]] 渲染出的链接带尾部 /,与 Django APPEND_SLASH 行为保持一致;关闭可少一次重定向,但要求路由本身不带斜杠匹配。

5.2 编辑器与 Markdown 渲染

WIKI_EDITOR:实测当前生效编辑器:

current editor: wiki.editors.markitup MarkItUp

WIKI_MARKDOWN_KWARGS:控制 Python-Markdown 扩展。实测生效值(逐行照抄,注意 extensions 列表中 footnotes/attr_list 出现两次——这是 0.13.0 源码 wiki/conf/settings.py 的原始状态,重复项不影响行为):

MARKDOWN_KWARGS: {'extensions': ['markdown.extensions.footnotes', 'markdown.extensions.attr_list', 'markdown.extensions.footnotes', 'markdown.extensions.attr_list', 'markdown.extensions.def_list', 'markdown.extensions.tables', 'markdown.extensions.abbr', 'markdown.extensions.sane_lists'], 'extension_configs': {'wiki.plugins.macros.mdx.toc': {'title': 'Contents'}}}

覆写方式(源码 MARKDOWN_KWARGS.update(getattr(django_settings, "WIKI_MARKDOWN_KWARGS", {}))):项目里写 WIKI_MARKDOWN_KWARGS = {...} 会合并进默认 dict。例如改目录标题:

WIKI_MARKDOWN_KWARGS = {
    "extension_configs": {
        "wiki.plugins.macros.mdx.toc": {"title": "Contents of this article"},
    },
}

WIKI_MARKDOWN_SANITIZE_HTML(默认 True):是否启用 bleach 净化。强烈建议保持开启——django-wiki 的 XSS 防线全靠它。实测白名单(37 个标签,sorted(w.MARKDOWN_HTML_WHITELIST) 输出):

whitelist size: 37
sorted whitelist: ['a', 'abbr', 'acronym', 'b', 'blockquote', 'br', 'code', 'dd', 'div', 'dl', 'dt', 'em', 'figcaption', 'figure', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'li', 'ol', 'p', 'pre', 'span', 'strong', 'sup', 'table', 'tbody', 'td', 'th', 'thead', 'tr', 'ul']

WIKI_MARKDOWN_HTML_ATTRIBUTES:按标签允许属性。默认构建逻辑(源码逐行照抄):

_default_attribute_whitelist = bleach.ALLOWED_ATTRIBUTES
for tag in MARKDOWN_HTML_WHITELIST:
    if tag not in _default_attribute_whitelist:
        _default_attribute_whitelist[tag] = []
    _default_attribute_whitelist[tag].append("class")
    _default_attribute_whitelist[tag].append("id")
    _default_attribute_whitelist[tag].append("target")
    _default_attribute_whitelist[tag].append("rel")

_default_attribute_whitelist["img"].append("src")
_default_attribute_whitelist["img"].append("alt")
_default_attribute_whitelist["td"].append("align")

即所有白名单标签都额外允许 class/id/target/rel,img 允许 src/alt,td 允许 align。

WIKI_MARKDOWN_HTML_STYLES(默认 []):允许的行内样式属性,默认全禁。源码联动逻辑:开启样式且未自定义属性白名单时,自动给全部标签加 style:

if MARKDOWN_HTML_STYLES and not _project_defined_attrs:
    MARKDOWN_HTML_ATTRIBUTES["*"] = "style"

净化效果实测:通过 /_create/ 表单创建文章 /xss-demo/,正文注入 <script>alert(1)</script>、onerror 事件、javascript: 伪协议与外部链接,渲染页实测:

POST: 302 /xss-demo/
GET /xss-demo/: 200
contains alert(1)      : True   (仅作为纯文本出现)
contains onerror       : False
contains javascript:   : False
contains evil.example  : False
渲染正文: <div class="wiki-article">alert(1)</div>

<script> 标签被剥离、仅保留文本,事件属性与伪协议链接整体删除——白名单机制按预期工作。

5.3 权限与账户体系

八个 WIKI_CAN_* 可调用参数(CAN_READ/CAN_WRITE/CAN_ASSIGN/CAN_ASSIGN_OWNER/CAN_CHANGE_PERMISSIONS/CAN_DELETE/CAN_MODERATE/CAN_ADMIN)默认全部 None,此时使用 django-wiki 内置的逐文章权限模型(Article 上的 group_read/group_write/other_read/other_write 字段 + 每对象权限)。设为自定义函数后,该函数接管对应权限的全局判定(源码注释:函数无需处理匿名用户,匿名由独立参数控制)。典型用法——只允许管理员编辑:

WIKI_CAN_WRITE = lambda user, article=None: user.is_staff

匿名四件套:

  • WIKI_ANONYMOUS(默认 True):匿名按 "other" 组对待;设 False 则所有匿名访问强制走登录。
  • WIKI_ANONYMOUS_WRITE(默认 False):全局放开匿名编辑(配合文章的 other_write)。
  • WIKI_ANONYMOUS_CREATE(默认跟随 WRITE):匿名能否新建文章。
  • WIKI_ANONYMOUS_UPLOAD(默认 False):匿名能否上传,附件/图片插件默认继承此值。

账户处理:WIKI_ACCOUNT_HANDLING(默认 True)提供注册/登录/注销视图;WIKI_ACCOUNT_SIGNUP_ALLOWED(默认 True)控制是否允许自助注册(关闭后登录的 superuser 仍可访问注册页建号)。URL 指向逻辑(源码):

if ACCOUNT_HANDLING:
    LOGIN_URL = reverse_lazy("wiki:login")
    LOGOUT_URL = reverse_lazy("wiki:logout")
    SIGNUP_URL = reverse_lazy("wiki:signup")
else:
    LOGIN_URL = getattr(django_settings, "LOGIN_URL", "/")
    LOGOUT_URL = getattr(django_settings, "LOGOUT_URL", "/")
    SIGNUP_URL = getattr(django_settings, "WIKI_SIGNUP_URL", "/")

工程 settings.py 还需配合 LOGIN_REDIRECT_URL,本项目实测使用:

LOGIN_REDIRECT_URL = reverse_lazy("wiki:get", kwargs={"path": ""})

WIKI_GROUP_MODEL(默认 'auth.Group'):权限关联的组模型;使用自定义 Group 模型的项目在此声明。

5.4 反滥用限流

五个参数构成两级限流(按「用户或 IP」计数):

参数默认值含义
WIKI_REVISIONS_PER_HOUR60登录用户每小时上限
WIKI_REVISIONS_PER_MINUTES5登录用户每分钟上限
WIKI_REVISIONS_PER_HOUR_ANONYMOUS10匿名每小时上限
WIKI_REVISIONS_PER_MINUTES_ANONYMOUS2匿名每分钟上限
WIKI_REVISIONS_MINUTES_LOOKBACK2分钟级限流的回看窗口

限流实测:将分钟级上限临时压到 2(回看窗口 2 分钟),用普通用户 editor1(密码 q1w2e3r4)连续提交 3 次编辑,验证脚本核心逻辑为运行时覆写后走 Django 测试客户端提交编辑表单:

ws.REVISIONS_PER_MINUTES = 2
ws.REVISIONS_MINUTES_LOOKBACK = 2

实测回显:

patched: REVISIONS_PER_MINUTES = 2
editor1 created: False superuser: False
login editor1: True
edit #1: status=302 spam_msg=None
edit #2: status=302 spam_msg=None
edit #3: status=200 spam_msg=Spam protection: You are only allowed to create or edit 2 article(s) per 2 minutes.

第 3 次编辑被拦下(返回 200 而非 302,页面显示 Spam protection 提示)。生产环境按团队编辑密度调大 WIKI_REVISIONS_PER_MINUTES(如 10)即可,无需关闭限流。

5.5 存储、附件与图片

全局存储:

  • WIKI_STORAGE_BACKEND:默认取 Django default_storage(本项目即 FileSystemStorage 落 MEDIA_ROOT=/srv/wiki/media);可指向 S3 等远端后端。
  • WIKI_ATTACHMENTS_USE_SENDFILE(默认 False):关闭时附件由 Django 视图整体读入内存再发送;开启需安装配置 django-sendfile 并由 Web 服务器(Nginx/Apache)代发,大文件场景建议开启。

附件插件(WIKI_ATTACHMENTS_*),默认值均取自实测源码 wiki/plugins/attachments/settings.py:

参数默认值作用
WIKI_ATTACHMENTS_PATH'wiki/attachments/%aid/'存储路径,%aid 替换为文章 id
WIKI_ATTACHMENTS_PATH_OBSCURIFYTrue路径加随机哈希混淆,防猜测文件位置
WIKI_ATTACHMENTS_EXTENSIONS['pdf', 'doc', 'odt', 'docx', 'txt']扩展名白名单,空列表 = 禁止上传
WIKI_ATTACHMENTS_APPEND_EXTENSIONTrue存储时追加 .upload 后缀,防服务器执行脚本
WIKI_ATTACHMENTS_LOCAL_PATHTrue本地路径模式;S3 等无 .path 属性的后端设 False
WIKI_ATTACHMENTS_ANONYMOUS继承 WIKI_ANONYMOUS_UPLOAD(False)匿名上传开关
WIKI_ATTACHMENTS_STORAGE_BACKEND继承 WIKI_STORAGE_BACKEND附件独立存储后端

⚠️ 注意:扩展名参数键名是 WIKI_ATTACHMENTS_EXTENSIONS,源码注释专门强调不是 WIKI_ATTACHMENTS_FILE_EXTENTIONS——写错键名不报错但也不生效。

图片插件(WIKI_IMAGES_*):

参数默认值作用
WIKI_IMAGES_PATH'wiki/images/%aid/'图片存储路径
WIKI_IMAGES_THUMBNAIL_SIZES{'default': '250x250', 'small': '150x150', 'medium': '300x300', 'large': '500x500', 'orig': None}缩略图档位;markdown 图片标签未指定档位时用 default
WIKI_IMAGES_PATH_OBSCURIFYTrue路径混淆
WIKI_IMAGES_ANONYMOUS继承 WIKI_ANONYMOUS_UPLOAD(False)匿名上传开关
WIKI_IMAGES_STORAGE_BACKEND继承 WIKI_STORAGE_BACKEND图片独立存储后端

5.6 缓存与杂项

  • WIKI_CACHE_TIMEOUT(默认 600 秒):文章渲染缓存超时。文章编辑会自动刷新缓存,但正文若由其他对象动态生成(宏、插件),最长要等这个周期才可见更新;3.4 节已实测覆写为 300 并验证生效。
  • WIKI_SHOW_MAX_CHILDREN(默认 20):侧栏菜单最多直接展示多少子文章,超出折叠为 "+more"。源码注释明确警告:NEVER set this to 0——会把「还有子文章」误报为「没有」,导致误删父文章。
  • WIKI_USE_BOOTSTRAP_SELECT_WIDGET(默认 True):不使用 Bootstrap 的主题可关闭。
  • WIKI_MESSAGE_TAG_CSS_CLASS:Django messages 级别到 CSS 类的映射(实测生效值:{10: 'alert alert-info', 40: 'alert alert-danger', 20: 'alert alert-info', 25: 'alert alert-success', 30: 'alert alert-warning'})。
  • WIKI_LOG_IPS_ANONYMOUS(默认 True)/ WIKI_LOG_IPS_USERS(默认 False):是否把编辑者 IP 记入修订记录。匿名可编辑的公开 wiki 建议保持记录匿名 IP(反破坏溯源);对登录用户记 IP 涉及隐私合规(GDPR 等),默认关闭。

5.7 通知子系统(django_nyt 的 NYT_* 与 Django EMAIL_*)

通知插件的订阅/邮件调度由依赖包 django_nyt 提供,参数在 django_nyt/conf.py 定义,同样支持在项目 settings 里用原名覆写(NYT_ 前缀)。实测默认值全表:

参数默认值作用
NYT_SEND_EMAILSTrue邮件通知总开关
NYT_EMAIL_SENDER'notifications@example.com'通知邮件发件人(务必改成邮件网关允许的地址)
NYT_INTERVALS((-1, 'never'), (0, 'instantly'), (1380, 'daily'), (9660, 'weekly'))用户可选的订阅频率(分钟;-1 表示禁用邮件)
NYT_INTERVALS_DEFAULT0(即时)新订阅默认频率
NYT_ENABLE_ADMINFalse是否在 Django admin 注册 nyt 模型
NYT_SEND_ONLY_LATESTFalse每订阅只发最新一条通知
NYT_EMAIL_SUBJECTNone硬编码统一邮件主题
NYT_EMAIL_TEMPLATE_DEFAULT'notifications/emails/default.txt'邮件正文模板
NYT_EMAIL_SUBJECT_TEMPLATE_DEFAULT'notifications/emails/default_subject.txt'邮件主题模板
NYT_DB_TABLE_PREFIX'nyt'数据表前缀(勿轻改)
NYT_USER_MODEL跟随 AUTH_USER_MODEL通知关联的用户模型

邮件实际投递走 Django 标准邮件配置(EMAIL_HOST/EMAIL_PORT/EMAIL_HOST_USER 等),且不在 Web 进程内发送,需要周期性执行管理命令 notifymail(见「七、常见问题」7.6 的连带实测)。未配置 EMAIL_HOST 时 Django 默认连 localhost:25,实测直接报错:

ConnectionRefusedError: [Errno 111] Connection refused

另有通知插件自身的键 ARTICLE_EDIT = "article_edit"(通知类型标识,源码注释:改动会破坏既有通知,不建议覆写)。

5.8 其余插件参数

插件参数默认值作用
分节编辑WIKI_EDITSECTION_MAX_LEVEL3到几级标题为止加 [edit] 链接
分节编辑WIKI_EDITSECTION_LINK_TEXT'[edit]'分节编辑链接文字
链接WIKI_LINKS_LOOKUP_LEVEL2相对 slug 找不到文章时,「创建该文章」链接的挂靠层级
宏WIKI_PLUGINS_METHODS('article_list', 'toc')宏插件启用的 markdown 宏

六、生产配置模板

适用场景:4GB 内存、40GB SSD、单节点部署、内网团队知识库(登录使用、少量匿名只读、gunicorn 2 worker)。追加到工程 settings.py:

# ===== 基础安全 =====
DEBUG = False
ALLOWED_HOSTS = ["wiki.example.internal"]
# SECRET_KEY 用下面命令生成后填入,勿用示例值:
# python -c "from django.core.management.utils import get_random_secret_key as g; print(g())"
SECRET_KEY = "<生成的随机串>"

# ===== 权限与账户:内网团队,禁自助注册,匿名只读 =====
WIKI_ACCOUNT_HANDLING = True
WIKI_ACCOUNT_SIGNUP_ALLOWED = False      # 由管理员建号
WIKI_ANONYMOUS = True                    # 匿名可读
WIKI_ANONYMOUS_WRITE = False             # 匿名不可写(默认值,显式声明)
WIKI_ANONYMOUS_UPLOAD = False

# ===== 反滥用:放宽到团队正常编辑密度 =====
WIKI_REVISIONS_PER_MINUTES = 10
WIKI_REVISIONS_PER_HOUR = 200
WIKI_REVISIONS_PER_MINUTES_ANONYMOUS = 2
WIKI_REVISIONS_PER_HOUR_ANONYMOUS = 10

# ===== 渲染安全:保持默认白名单净化 =====
WIKI_MARKDOWN_SANITIZE_HTML = True
WIKI_MARKDOWN_HTML_STYLES = []

# ===== 附件与图片 =====
WIKI_ATTACHMENTS_EXTENSIONS = ["pdf", "doc", "docx", "xlsx", "pptx", "txt", "png", "jpg"]
WIKI_ATTACHMENTS_PATH_OBSCURIFY = True
WIKI_IMAGES_PATH_OBSCURIFY = True
# 大附件多时配合 Nginx 开启 sendfile(需安装 django-sendfile):
# WIKI_ATTACHMENTS_USE_SENDFILE = True

# ===== 缓存 =====
WIKI_CACHE_TIMEOUT = 300

# ===== 通知邮件 =====
NYT_SEND_EMAILS = True
NYT_EMAIL_SENDER = "wiki-notifications@example.internal"
EMAIL_HOST = "smtp.example.internal"
EMAIL_PORT = 25

# ===== TLS 由反向代理终结时补充(真机 HTTPS 场景)=====
# SESSION_COOKIE_SECURE = True
# CSRF_COOKIE_SECURE = True
# SECURE_SSL_REDIRECT = True
# SECURE_HSTS_SECONDS = 31536000

模板落地前用 Django 自检对照。当前测试环境(DEBUG=True、无 HTTPS)实测 manage.py check --deploy 回显 6 条警告:

WARNINGS:
?: (security.W004) You have not set a value for the SECURE_HSTS_SECONDS setting. If your entire site is served only over SSL, you may want to consider setting a value and enabling HTTP Strict Transport Security. Be sure to read the documentation first; enabling HSTS carelessly can cause serious, irreversible problems.
?: (security.W008) Your SECURE_SSL_REDIRECT setting is not set to True. Unless your site should be available over both SSL and non-SSL connections, you may want to either set this setting True or configure a load balancer or reverse-proxy server to redirect all connections to HTTPS.
?: (security.W009) Your SECRET_KEY has less than 50 characters, less than 5 unique characters, or it's prefixed with 'django-insecure-' indicating that it was generated automatically by Django. Please generate a long and random value, otherwise many of Django's security-critical features will be vulnerable to attack.
?: (security.W012) SESSION_COOKIE_SECURE is not set to True. Using a secure-only session cookie makes it more difficult for network traffic sniffers to hijack user sessions.
?: (security.W016) You have 'django.middleware.csrf.CsrfViewMiddleware' in your MIDDLEWARE, but you have not set CSRF_COOKIE_SECURE to True. Using a secure-only CSRF cookie makes it more difficult for network traffic sniffers to steal the CSRF token.
?: (security.W018) You should not have DEBUG set to True in deployment.

System check identified 6 issues (0 silenced).

其中 W009/W018 由模板的随机 SECRET_KEY 与 DEBUG = False 直接消除;W004/W008/W012/W016 对应启用 HTTPS(或反代终结 TLS)后打开模板末尾注释的四个 SECURE_*/*_COOKIE_SECURE 设置。

七、常见问题

7.1 改了 WIKI_ 参数不生效

按序排查三种原因:

  1. 键名少了 WIKI_ 前缀:项目里写 EDITOR = ... 不会被 django-wiki 读取(那是 Django 保留键),必须写 WIKI_EDITOR。
  2. 服务未重启:gunicorn 进程仍持有旧 settings,见 3.4 节;改完先用 manage.py shell 验证生效值,再重启。
  3. 插件参数用了错的前缀:附件参数是 WIKI_ATTACHMENTS_*、图片是 WIKI_IMAGES_*,通知是 NYT_*;尤其注意源码注释点名的拼写陷阱——是 WIKI_ATTACHMENTS_EXTENSIONS,不是 WIKI_ATTACHMENTS_FILE_EXTENTIONS。

7.2 上传附件被拒绝

两层拦截:文件扩展名必须命中 WIKI_ATTACHMENTS_EXTENSIONS 白名单(默认只有 pdf/doc/odt/docx/txt 五种,其他扩展名一律拒收);存储时默认追加 .upload 后缀(WIKI_ATTACHMENTS_APPEND_EXTENSION,防 Web 服务器执行上传脚本),下载时由 Django 视图按真实 MIME 类型发送,属正常现象不是损坏。需要更多类型就显式扩白名单,不要把白名单设成空列表(空 = 完全禁止上传)。

7.3 ImproperlyConfigured: not USE_LOCAL_PATH and APPEND_EXTENSION

把 WIKI_ATTACHMENTS_LOCAL_PATH = False(S3 等无本地路径的后端)而 WIKI_ATTACHMENTS_APPEND_EXTENSION 仍为 True 时,插件在导入期直接抛错(源码逐行照抄):

django-wiki (attachment plugin) not USE_LOCAL_PATH and APPEND_EXTENSION:
You have configured to append .upload and not use local paths. That won't
work as all your attachments will be stored and sent with a .upload
extension. You have to trust your storage backend to be safe for storing
the extensions you have allowed.

二选一:远端存储保持 APPEND_EXTENSION = False(信任后端安全),或回到本地路径模式。

7.4 编辑被 Spam protection 拦截

见 5.4 节实测:提示 Spam protection: You are only allowed to create or edit 2 article(s) per 2 minutes. 说明命中 WIKI_REVISIONS_PER_MINUTES(默认 5)×WIKI_REVISIONS_MINUTES_LOOKBACK(默认 2 分钟)窗口。等窗口过去即可恢复;批量导入/迁移场景临时调大 WIKI_REVISIONS_PER_MINUTES 与 WIKI_REVISIONS_PER_HOUR 再重启服务。

7.5 自定义 HTML/样式被剥离

默认白名单净化(5.2 节实测 37 标签、样式全禁)。需要放行额外标签/属性/样式时,在工程 settings 增量配置 WIKI_MARKDOWN_HTML_WHITELIST(frozenset 并集)、WIKI_MARKDOWN_HTML_ATTRIBUTES(dict 合并)、WIKI_MARKDOWN_HTML_STYLES(如 ['color', 'text-align']);每放开一项都是 XSS 攻击面,放开样式后源码会自动给全部标签追加 style 属性(见 5.2 联动逻辑),务必确认编辑者可信。

7.6 执行 wiki_notifications_create_defaults 报 ValueError: A user must have a default settings object

现象(实测完整 traceback 节选,逐行照抄关键帧):

  File "/srv/wiki/venv/lib/python3.12/site-packages/wiki/plugins/notifications/management/commands/wiki_notifications_create_defaults.py", line 26, in subscribe_to_article
    settings_map[user], __ = Settings.objects.get_or_create(
  File "/srv/wiki/venv/lib/python3.12/site-packages/django/db/models/query.py", line 669, in create
    obj.save(force_insert=True, using=self.db)
  File "/srv/wiki/venv/lib/python3.12/site-packages/django_nyt/models.py", line 238, in save
    raise ValueError("A user must have a default settings object")
ValueError: A user must have a default settings object

定位:命令源码用 Settings.objects.get_or_create(user=user) 为「文章所有者/编辑者」补通知设置,但没带 is_default=True;而 django_nyt.Settings.save() 规定:新建非默认行时,若该用户还没有任何默认设置行就抛 ValueError。因此只要有一篇文章的所有者或修订者从未初始化过通知设置(典型为脚本/命令行创建的用户,如 createsuperuser、admin 后台建号),命令就会崩。

解决:先给所有涉及用户补默认设置行,再重跑。准备脚本 fix_nyt.py:

from django.contrib.auth import get_user_model
from django_nyt.models import Settings
from wiki.models import Article

U = get_user_model()
involved = set()
for a in Article.objects.all():
    if a.owner:
        involved.add(a.owner)
    for r in a.articlerevision_set.exclude(user=None).values("user"):
        involved.add(U.objects.get(id=r["user"]))
print("involved users:", sorted(u.username for u in involved))
missing = [u for u in involved if not Settings.objects.filter(user=u).exists()]
print("users WITHOUT any Settings row:", [u.username for u in missing])
for u in missing:
    Settings.objects.create(user=u, is_default=True)
print("after fix:", list(Settings.objects.values("user__username", "is_default")))

执行并重跑命令(实测回显):

./venv/bin/python manage.py shell < fix_nyt.py
./venv/bin/python manage.py wiki_notifications_create_defaults
32 objects imported automatically (use -v 2 for details).

involved users: ['admin', 'editor1']
users WITHOUT any Settings row: ['editor1']
after fix: [{'user__username': 'admin', 'is_default': True}, {'user__username': 'editor2', 'is_default': True}, {'user__username': 'editor1', 'is_default': True}]
Created 8 subscriptions on  4 articles

💡 说明:经 wiki 注册页(signup)创建的用户会自动获得默认设置,不会触发本问题;只有绕过注册流程建号并直接编辑过文章的存量用户需要按上面补一次。

八、参考链接

django-wiki 0.13.0 配置参数详解 - Quzuo