Wagtail 4.1添加子页面报错:'NoneType' object has no attribute '_inc_path'
问题:集成Wagtail 4.1到Django项目时添加子页面报错
'NoneType' object has no attribute '_inc_path' 尝试将Wagtail 4.1集成到现有Django项目中,添加子页面时触发错误:'NoneType' object has no attribute '_inc_path'。
已完成的配置
1. 设置文件中的INSTALLED_APPS与MIDDLEWARE
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.humanize', 'whitenoise.runserver_nostatic', 'home', 'news', 'wagtail.contrib.forms', 'wagtail.contrib.redirects', 'wagtail.embeds', 'wagtail.sites', 'wagtail.users', 'wagtail.snippets', 'wagtail.documents', 'wagtail.images', 'wagtail.search', 'wagtail.admin', 'wagtail', 'modelcluster', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django_permissions_policy.PermissionsPolicyMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'corsheaders.middleware.CorsMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'wagtail.contrib.redirects.middleware.RedirectMiddleware', ]
2. 项目urls配置
from django.urls import include, path from django.conf import settings from django.conf.urls.static import static from django.urls import reverse from wagtail.admin import urls as wagtailadmin_urls from wagtail import urls as wagtail_urls from wagtail.documents import urls as wagtaildocs_urls urlpatterns = [ path('', include('home.urls')), path('news/', include('news.urls')), path('cms/', include(wagtailadmin_urls)), path('documents/', include(wagtaildocs_urls)), path('news/', include(wagtail_urls)), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
3. news应用的urls配置
from django.urls import path from .views import news_article_detail urlpatterns = [ path('news/<int:pk>/', news_article_detail, name='news_article_detail'), ]
4. news视图代码
from django.shortcuts import render from .models import NewsArticlePage def news_article_detail(request, pk): news_article = NewsArticlePage.objects.get(pk=pk) return render(request, 'news/news_page.html', {'news_article': news_article})
5. NewsArticlePage模型代码
from django.db import models from wagtail.core.models import Page from wagtail.admin.edit_handlers import FieldPanel from wagtail.core.fields import RichTextField class NewsArticlePage(Page): article_body = RichTextField(blank=True) content_panels = Page.content_panels + [ FieldPanel('article_body'), ] template = "news/news_page.html"
6. 模板代码
{% extends "base.html" %} {% block content %} <h1>{{ page.title }}</h1> {{ page.article_body|richtext }} {% endblock %}
已做的排查操作
- 创建根页面
- 确认路由指向正确
- 执行数据库迁移
- 尝试
python manage.py fixtree命令
但添加子页面时仍触发错误,堆栈信息如下:
Environment: Request Method: POST Request URL: http://127.0.0.1:7000/cms/pages/add/news/newsarticlepage/2/ Django Version: 3.2.4 Python Version: 3.11.1 Installed Applications: ['django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'django.contrib.humanize', 'whitenoise.runserver_nostatic', 'accounts', 'compressor', 'corsheaders', 'home', 'news', 'taggit', 'widget_tweaks', 'sass_processor', 'wagtail.contrib.forms', 'wagtail.contrib.redirects', 'wagtail.embeds', 'wagtail.sites', 'wagtail.users', 'wagtail.snippets', 'wagtail.documents', 'wagtail.images', 'wagtail.search', 'wagtail.admin', 'wagtail', 'modelcluster'] Installed Middleware: ['django.middleware.security.SecurityMiddleware', 'django_permissions_policy.PermissionsPolicyMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'corsheaders.middleware.CorsMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'wagtail.contrib.redirects.middleware.RedirectMiddleware'] Traceback (most recent call last): File "C:\Users\sesa301575\sites\Django.Azure\venv\Lib\site-packages\django\core\handlers\exception.py", line 47, in inner response = get_response(request) File "C:\Users\sesa301575\sites\Django.Azure\venv\Lib\site-packages\django\core\handlers\base.py", line 181, in _get_response response = wrapped_callback(request, *callback_args, **callback_kwargs) File "C:\Users\sesa301575\sites\Django.Azure\venv\Lib\site-packages\django\views\decorators\cache.py", line 44, in _wrapped_view_func response = view_func(request, *args, **kwargs) File "C:\Users\sesa301575\sites\Django.Azure\venv\Lib\site-packages\wagtail\admin\urls\__init__.py", line 170, in wrapper return view_func(request, *args, **kwargs) File "C:\Users\sesa301575\sites\Django.Azure\venv\Lib\site-packages\wagtail\admin\auth.py", line 182, in decorated_view response = view_func(request, *args, **kwargs) File "C:\Users\sesa301575\sites\Django.Azure\venv\Lib\site-packages\django\views\generic\base.py", line 70, in view return self.dispatch(request, *args, **kwargs) File "C:\Users\sesa301575\sites\Django.Azure\venv\Lib\site-packages\wagtail\admin\views\pages\create.py", line 119, in dispatch return super().dispatch(request) File "C:\Users\sesa301575\sites\Django.Azure\venv\Lib\site-packages\django\views\generic\base.py", line 98, in dispatch return handler(request, *args, **kwargs) File "C:\Users\sesa301575\sites\Django.Azure\venv\Lib\site-packages\wagtail\admin\views\pages\create.py", line 132, in post return self.form_valid(self.form) File "C:\Users\sesa301575\sites\Django.Azure\venv\Lib\site-packages\wagtail\admin\views\pages\create.py", line 148, in form_valid return self.save_action() File "C:\Users\sesa301575\sites\Django.Azure\venv\Lib\site-packages\wagtail\admin\views\pages\create.py", line 170, in save_action self.parent_page.add_child(instance=self.page) File "C:\Users\sesa301575\sites\Django.Azure\venv\Lib\site-packages\treebeard\mp_tree.py", line 1089, in add_child return MP_AddChildHandler(self, **kwargs).process() File "C:\Users\sesa301575\sites\Django.Azure\venv\Lib\site-packages\treebeard\mp_tree.py", line 383, in process newobj.path = self.node.get_last_child()._inc_path() Exception Type: AttributeError at /cms/pages/add/news/newsarticlepage/2/ Exception Value: 'NoneType' object has no attribute '_inc_path'
解决方案
从堆栈信息可以看出,错误出现在treebeard的MPTT处理逻辑中:self.node.get_last_child()返回了None,导致调用_inc_path()时触发AttributeError。这说明父页面(ID为2的页面)的树结构存在异常,以下是具体修复步骤:
1. 检查父页面的树结构字段
通过Django Shell查看父页面的path、depth、numchild字段是否合法:
python manage.py shell
from wagtail.core.models import Page parent = Page.objects.get(id=2) print(f"Path: {parent.path}, Depth: {parent.depth}, Numchild: {parent.numchild}")
正常情况下,path是类似0001、00010001的字符串,depth对应页面在树中的层级(根页面为1),numchild是子页面数量。如果这些字段为空或格式异常,需要手动修复或重新生成。
2. 重新执行完整的fixtree命令
默认的fixtree可能只处理部分页面,加上--all参数强制修复所有页面的树结构:
python manage.py fixtree --all
3. 调整URL路由顺序
项目urls中,Wagtail的wagtail_urls应该放在自定义的news.urls之后,否则会优先匹配自定义路由,导致Wagtail页面路由失效(虽然不是当前报错的直接原因,但会影响后续页面访问):
urlpatterns = [ path('', include('home.urls')), path('cms/', include(wagtailadmin_urls)), path('documents/', include(wagtaildocs_urls)), path('news/', include('news.urls')), path('news/', include(wagtail_urls)), # 移到自定义news路由之后 ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
4. 排查自定义逻辑干扰
检查是否有自定义的信号、模型方法或中间件在页面保存时修改了MPTT相关字段(如path、depth),这类操作可能破坏树结构。
5. 重新创建父页面
如果以上方法都无效,可以尝试删除当前父页面(ID为2),重新创建一个新的父页面,再尝试添加子页面。
内容的提问来源于stack exchange,提问作者Who Me Dunno Mate
相关产品推荐
相关产品推荐

