将 Wagtail 集成到现有 Django 项目:settings.py 与 urls.py 完整配置指南
【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtail
Wagtail 是一个强调灵活性与用户体验的 Django 内容管理系统,其核心设计理念是"可选的模块化"——即使项目最初不是用 Wagtail 起步,也能通过少量配置将其平滑地引入既有 Django 工程。本文基于 docs/advanced_topics/add_to_django_project.md 展开,完整讲解在已有 Django 项目中接入 Wagtail 所需的settings.py中间件与应用列表、urls.py的 URL 分发规则,并结合仓库中的真实模板与源码(如 wagtail/project_template/project_name/settings/base.py)逐项解释每个配置的作用。读完本文,你将掌握一套可直接复制运行的 Wagtail + Django 最小配置,并能根据项目需求增删 Wagtail 子应用、理解RedirectMiddleware与 URL 兜底分发的底层原理。
前置准备:在 Django 工程中腾出 Wagtail 的位置
假设你已按 Django 官方教程创建了一个标准工程(django-admin startproject与python manage.py startapp),目录结构如下:
myproject/ myproject/ __init__.py settings.py urls.py wsgi.py myapp/ __init__.py models.py tests.py admin.py views.py manage.pyWagtail 会为自己的页面模型、文档、图片等提供完整的后台管理界面,因此从你的业务应用myapp/中可以安全地删除admin.py和views.py——这些功能将由 Wagtail 的 admin 与页面渲染机制接管。剩余的models.py用来定义你的业务模型(尤其是继承Page的页面模型),tests.py用来编写测试。
注:仓库内的官方项目脚手架见 wagtail/project_template,其中
home、search应用即为标准 Wagtail 工程的形态,可作为对照参考。真实 Django 工程的模板由 wagtail/project_template/project_name/settings/base.py 与 wagtail/project_template/project_name/urls.py 体现。
Middleware 配置:在基础中间件之上叠加重定向能力
Wagtail 依赖 Django 的默认中间件集合来保证基础安全与会话登录等功能。在settings.py的MIDDLEWARE中加入 Wagtail 提供的额外中间件:
MIDDLEWARE = [ "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", "django.middleware.security.SecurityMiddleware", "wagtail.contrib.redirects.middleware.RedirectMiddleware", ]RedirectMiddleware:让"旧路径 → 新地址"的重定向生效
Wagtail 提供了一个简单易用的重定向管理界面,而RedirectMiddleware负责在请求层真正执行这些重定向。其实现位于 wagtail/contrib/redirects/middleware.py,核心逻辑如下:
- 通过
process_response拦截状态码为 404的响应(非 404 响应直接放行,避免无谓开销); - 将请求路径交给
Redirect.get_for_site()按站点匹配old_path,支持"全站通用重定向"(site=None)与"站点专属重定向"两种记录,两者冲突时优先站点专属记录(见 wagtail/contrib/redirects/models.py 的MultipleObjectsReturned分支); - 匹配成功后,根据
is_permanent字段返回HttpResponsePermanentRedirect(301,永久)或HttpResponseRedirect(302,临时)。该字段默认值为True,官方建议永久重定向能让搜索引擎忘记旧页面、索引新页面; - 重定向目标既可以是站内页面(
redirect_page,还可通过redirect_page_route_path指定目标页内的具体路由),也可以是任意 URL(redirect_link),两者都未设置时link属性返回None,中间件直接放行。
值得一提的细节:中间件会先用uri_to_iri解码百分号编码的路径再匹配,若解码后未命中还会回退到原始编码路径尝试一次;同时对包含空字符(\0)的 URL 直接拒绝匹配,这是为了防止在 Postgres 上触发崩溃(issue #4496)。
INSTALLED_APPS 配置:模块化应用清单
Wagtail 是高度模块化的,其INSTALLED_APPS清单分为三部分:你自己的业务应用、Wagtail 自带应用、第三方依赖应用:
INSTALLED_APPS = [ "myapp", # your own app "wagtail.contrib.forms", "wagtail.contrib.redirects", "wagtail.embeds", "wagtail.sites", "wagtail.users", "wagtail.snippets", "wagtail.documents", "wagtail.images", "wagtail.search", "wagtail.admin", "wagtail", "taggit", "modelcluster", "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", ]你的应用(示例中的myapp)是定义页面模型、模板、静态资源、模板标签和站点自定义功能的地方,通常放在列表首位。
Wagtail 自带应用逐个解析
wagtail:Wagtail 核心功能——Page类、Wagtail 页面树与模型字段。核心页面模型定义在 wagtail/models,页面路由机制见 wagtail/urls.py 中views.serve对re_path(serve_pattern, views.serve)的处理;wagtail.admin:Wagtail 后台管理界面,包括页面编辑处理器(edit handlers)。其 URL 结构由 wagtail/admin/urls/init.py 定义,包含页面探索、选择器、工作流、报告、账户等大量子路由,并且所有视图默认经过require_admin_access权限检查与never_cache装饰;wagtail.documents:文档内容类型,上传、管理与前端服务文档。文档服务路由^(\d+)/(.*)$定义在 wagtail/documents/urls.py;wagtail.snippets:为"非 Page 模型"提供后台编辑界面,详见 docs/topics/snippets;wagtail.users:用户编辑界面;wagtail.images:图片内容类型,包含图片操作滤镜(image_operations.py)与后台管理;wagtail.embeds:管理富文本字段中的 oEmbed 与 Embedly 嵌入内容;wagtail.search:针对 Page 内容的搜索框架,详见 docs/topics/search/index.md;wagtail.sites:Wagtail 站点的管理 UI;wagtail.contrib.redirects:创建站点任意重定向的后台管理界面(配合上文RedirectMiddleware使用);wagtail.contrib.forms:在页面上创建表单并查看提交结果的模型,详见 docs/reference/contrib/forms/index.md 的 Form builder 说明。
由于 Wagtail 按模块组织,不需要的功能可以按需移除。例如不使用嵌入功能可去掉wagtail.embeds,不需要文档库可去掉wagtail.documents。
两个关键的第三方应用
taggit:Django 标签框架,Wagtail 内部用它实现图片与文档的标签管理,你的自有模型也可以直接复用。推荐配置TAGGIT_CASE_INSENSITIVE = True关闭标签大小写敏感(后文完整配置中已包含),Wagtail 模型中的标签用法可参考 docs/advanced_topics/tags.md;modelcluster:对 Django 外键关系的扩展,支撑 Wagtail 页面中"随页面即时创建关联对象"的能力(如 InlinePanel 内联子对象),相关用法见 docs/reference/panels.md 的inline_panels章节。
对比仓库脚手架 wagtail/project_template/project_name/settings/base.py,官方新工程模板还额外包含
home、search两个业务应用以及django_filters,其余 Wagtail 应用清单与本文完全一致——这印证了该清单就是 Wagtail 的标准基线配置。
URL Patterns:从 Django 应用到 Wagtail 的接管
Wagtail 的接入关键是让 Django 的 URL 分发按"精确优先、Wagtail 兜底"的顺序工作:
from django.contrib import admin from django.urls import include, path, re_path from wagtail import urls as wagtail_urls from wagtail.admin import urls as wagtailadmin_urls from wagtail.documents import urls as wagtaildocs_urls urlpatterns = [ path("django-admin/", admin.site.urls), path("admin/", include(wagtailadmin_urls)), path("documents/", include(wagtaildocs_urls)), # Optional URL for including your own vanilla Django urls/views re_path(r"", include("myapp.urls")), # For anything not caught by a more specific rule above, hand over to # Wagtail's serving mechanism re_path(r"", include(wagtail_urls)), ]这段配置完成了四件事:
- 将 Django 原生 admin 挂载到
/django-admin/(与 Wagtail 的/admin/区分,避免路由冲突); - 将 Wagtail 后台及其子应用挂载到
/admin/; - 可选:将你自己的普通 Django 视图/URL 配置(
myapp.urls)挂载进来——如果你的站点完全由 Wagtail 页面构成,这一行可以省略; - 让 Wagtail 接管所有未被上述规则捕获的 URL,进入页面渲染机制。
最后一步的底层实现值得展开:wagtail_urls来自 wagtail/urls.py,其兜底模式是一个正则re_path(serve_pattern, views.serve, name="wagtail_serve")。serve_pattern受WAGTAIL_APPEND_SLASH设置影响:
- 默认
WAGTAIL_APPEND_SLASH = True时,模式为^((?:[\w\-]+/)*)$,即只匹配以斜杠结尾的路径段序列,缺少尾斜杠的请求由CommonMiddleware自动 301 重定向补全; - 设置为
False时,模式变为^([\w\-/]*)$,允许 Wagtail 同时服务带或不带尾斜杠的 URL。
该文件还包含两个与前端相关的内置路由:_util/authenticate_with_password/...(页面浏览限制的密码验证)与_util/login/(页面级登录),登录模板可通过WAGTAIL_FRONTEND_LOGIN_TEMPLATE覆盖。
可直接运行的完整示例配置
下面给出两份开箱即用的完整配置文件,它们应放置在工程目录(myproject/myproject/)下,对应原文档中的(complete_example_config)小节。
完整版settings.py
from pathlib import Path PROJECT_DIR = Path(__file__).resolve().parent.parent BASE_DIR = PROJECT_DIR.parent DEBUG = True # Application definition INSTALLED_APPS = [ "myapp", "wagtail.contrib.forms", "wagtail.contrib.redirects", "wagtail.embeds", "wagtail.sites", "wagtail.users", "wagtail.snippets", "wagtail.documents", "wagtail.images", "wagtail.search", "wagtail.admin", "wagtail", "taggit", "modelcluster", "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", ] MIDDLEWARE = [ "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", "django.middleware.security.SecurityMiddleware", "wagtail.contrib.redirects.middleware.RedirectMiddleware", ] ROOT_URLCONF = "myproject.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 = "myproject.wsgi.application" # Database DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", "NAME": "myprojectdb", "USER": "postgres", "PASSWORD": "", "HOST": "", # Set to empty string for localhost. "PORT": "", # Set to empty string for default. "CONN_MAX_AGE": 600, # number of seconds database connections should persist for } } # Internationalization LANGUAGE_CODE = "en-us" TIME_ZONE = "UTC" USE_I18N = True USE_TZ = True # Static files (CSS, JavaScript, Images) 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/" ADMINS = [ # ('Your Name', 'your_email@example.com'), ] MANAGERS = ADMINS # Default to dummy email backend. Configure dev/production/local backend # as per Django docs on email backends. EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend" # Hosts/domain names that are valid for this site; required if DEBUG is False ALLOWED_HOSTS = [] # Make this unique, and don't share it with anybody. SECRET_KEY = "change-me" EMAIL_SUBJECT_PREFIX = "[Wagtail] " INTERNAL_IPS = ("127.0.0.1", "10.0.2.2") # A sample logging configuration. The only tangible logging # performed by this configuration is to send an email to # the site admins on every HTTP 500 error when DEBUG=False. LOGGING = { "version": 1, "disable_existing_loggers": False, "filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}}, "handlers": { "mail_admins": { "level": "ERROR", "filters": ["require_debug_false"], "class": "django.utils.log.AdminEmailHandler", } }, "loggers": { "django.request": { "handlers": ["mail_admins"], "level": "ERROR", "propagate": True, }, }, } # WAGTAIL SETTINGS # This is the human-readable name of your Wagtail install # which welcomes users upon login to the Wagtail admin. WAGTAIL_SITE_NAME = "My Project" # Replace the search backend # WAGTAILSEARCH_BACKENDS = { # 'default': { # 'BACKEND': 'wagtail.search.backends.elasticsearch8', # 'INDEX': 'myapp' # } # } # Wagtail email notifications from address # WAGTAILADMIN_NOTIFICATION_FROM_EMAIL = 'wagtail@myhost.io' # Wagtail email notification format # WAGTAILADMIN_NOTIFICATION_USE_HTML = True # 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. WAGTAILDOCS_EXTENSIONS = [ "csv", "docx", "key", "odt", "pdf", "pptx", "rtf", "txt", "xlsx", "zip", ] # Reverse the default case-sensitive handling of tags TAGGIT_CASE_INSENSITIVE = True完整版urls.py
from django.urls import include, path, re_path from django.conf.urls.static import static from django.views.generic.base import RedirectView from django.contrib import admin from django.conf import settings import os.path from wagtail import urls as wagtail_urls from wagtail.admin import urls as wagtailadmin_urls from wagtail.documents import urls as wagtaildocs_urls urlpatterns = [ path("django-admin/", admin.site.urls), path("admin/", include(wagtailadmin_urls)), path("documents/", include(wagtaildocs_urls)), # For anything not caught by a more specific rule above, hand over to # Wagtail's serving mechanism re_path(r"", include(wagtail_urls)), ] if settings.DEBUG: from django.contrib.staticfiles.urls import staticfiles_urlpatterns urlpatterns += ( staticfiles_urlpatterns() ) # tell gunicorn where static files are in dev mode urlpatterns += static( settings.MEDIA_URL + "images/", document_root=settings.MEDIA_ROOT / "images" ) urlpatterns += [ path( "favicon.ico", RedirectView.as_view(url=settings.STATIC_URL + "myapp/images/favicon.ico"), ) ]开发模式下(DEBUG = True)额外追加了三条规则:
staticfiles_urlpatterns():让开发服务器直接提供各应用与STATICFILES_DIRS下的静态文件;static(settings.MEDIA_URL + "images/", document_root=settings.MEDIA_ROOT / "images"):将MEDIA_ROOT/images/下的媒体文件(Wagtail 图片渲染生成的副本)以/media/images/前缀对外服务;favicon.ico:将站点图标请求重定向到myapp应用内的静态图标(STATIC_URL + "myapp/images/favicon.ico")。
这些仅服务于本地开发;生产环境应通过 Web 服务器(如 Nginx)或对象存储直接提供静态与媒体文件。
关键 Wagtail 配置项解读
对照仓库脚手架 wagtail/project_template/project_name/settings/base.py,下面几个 Wagtail 专属配置在集成时最常被调整:
| 配置项 | 作用 | 备注 |
|---|---|---|
WAGTAIL_SITE_NAME | 站点的人性化名称,显示在 Wagtail 后台登录欢迎页 | 必配,否则后台会告警 |
WAGTAILSEARCH_BACKENDS | 搜索后端配置 | 示例中注释掉的wagtail.search.backends.elasticsearch8为 Elasticsearch 8 后端;脚手架默认使用wagtail.search.backends.database(数据库后端) |
WAGTAILADMIN_NOTIFICATION_FROM_EMAIL | Wagtail 通知邮件的发件地址 | 默认继承DEFAULT_FROM_EMAIL |
WAGTAILADMIN_NOTIFICATION_USE_HTML | 通知邮件是否使用 HTML 格式 | 布尔值 |
WAGTAILADMIN_BASE_URL | 后台生成完整 URL(如通知邮件链接)时使用的基础地址,不含/admin与尾斜杠 | 脚手架示例为http://example.com |
WAGTAILDOCS_EXTENSIONS | 文档库允许上传的文件扩展名白名单 | 省略则允许所有文件,但允许不可信用户上传时存在安全风险(参见部署文档中 user-uploaded-files 一节) |
WAGTAILDOCS_MAX_UPLOAD_SIZE | 文档最大上传体积(字节) | 脚手架示例为 10MB |
TAGGIT_CASE_INSENSITIVE | 关闭标签大小写敏感 | 默认 Django taggit 为大小写敏感,此配置将其反转 |
脚手架中还包含两个值得注意的 Django 级配置:DATA_UPLOAD_MAX_NUMBER_FIELDS = 10_000(Django 默认单表单最多 1000 字段,而复杂页面模型在 Wagtail 页面编辑器中可能超出此限制,故放宽到 1 万);以及STORAGES中对default与staticfiles存储后端的显式声明。
集成后的下一步
完成上述配置后,运行迁移并创建管理员即可启用 Wagtail:
python manage.py migrate python manage.py createsuperuser python manage.py runserver随后访问http://127.0.0.1:8000/admin/进入 Wagtail 后台,访问http://127.0.0.1:8000/django-admin/仍可进入 Django 原生 admin。接下来你可以:
- 在
myapp/models.py中定义继承wagtail.models.Page的页面模型,通过content_panels/edit_handler声明后台编辑表单; - 参考 docs/getting_started/tutorial.md 完成从零构建博客站点的完整演练;
- 需要表单能力时阅读 docs/reference/contrib/forms/index.md 的 Form builder;需要标签功能时参考 docs/advanced_topics/tags.md;需要内联子对象时查阅 docs/reference/panels.md 的
inline_panels小节。
Wagtail 的集成哲学始终是"按需裁剪":上面列出的INSTALLED_APPS是一个完整基线,你可以只保留wagtail、wagtail.admin、wagtail.documents、wagtail.images等实际用到的模块,再配合RedirectMiddleware与 URL 兜底分发,让既有 Django 项目在最小改动下获得完整的 CMS 能力。
【免费下载链接】wagtailA Django content management system focused on flexibility and user experience项目地址: https://gitcode.com/GitHub_Trending/wa/wagtail
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考