Django多数据库操作报错:wagtailcore_page.draft_title非空约束失败
我正在开发一个集成Wagtail的Django项目,配置了两个SQLite数据库:
- 标准数据库
db_tool.sqlite3:存储所有自定义Django模型 - Wagtail专用数据库
db.sqlite3:存储Wagtail相关数据(使用Django默认数据库名称)
执行以下命令对db_tool数据库执行迁移:
python manage.py makemigrations python manage.py migrate --database db_tool
却出现 Wagtail 相关的完整性错误:
django.db.utils.IntegrityError: NOT NULL constraint failed: wagtailcore_page.draft_title
错误溯源到Wagtail核心迁移文件:
File "C:\Users\pubr\.conda\envs\iqps_web\lib\site-packages\wagtail\core\migrations\0001_squashed_0016_change_page_url_path_to_text_field.py", line 23, in initial_data root = Page.objects.create(
我的疑问:
- 明明指定了
db_tool数据库,为什么会触发Wagtail相关的报错? - 我的自定义Wagtail页面中没有
draft_title字段,且当前项目里没有草稿页面,为什么会报这个错? - 是否因为之前误操作,未指定数据库就执行了本该针对
db_tool的迁移,导致了这个问题?
项目使用Wagtail 2.15.2版本,自启动后未更新过。
问题原因分析
1. 指定数据库仍触发Wagtail报错的原因
Django的migrate --database参数指定的是执行迁移的目标数据库,但Wagtail的初始迁移(如0001_squashed_...)包含RunPython操作,其中直接调用了Page.objects.create()——如果没有明确配置数据库路由,这个ORM操作会默认使用Django默认数据库(即你的Wagtail专用db.sqlite3)。
如果默认数据库的Wagtail表结构不完整(比如之前迁移中断、部分执行),或者你之前误操作在db_tool数据库里创建了Wagtail的表,就会导致跨数据库冲突:当你对db_tool执行迁移时,Wagtail迁移里的初始化代码尝试在默认数据库创建根页面,但默认数据库的wagtailcore_page表存在draft_title非空约束,而初始化代码未提供该字段值(Wagtail旧版本迁移存在兼容性问题)。
2. draft_title字段的来源
draft_title是Wagtail核心Page模型的内置字段,并非你的自定义页面模型字段。即使你没有创建草稿页面,Wagtail的初始迁移会自动创建一个根页面(root = Page.objects.create()),这个操作需要填充所有非空字段,包括draft_title。
Wagtail 2.15.2的初始迁移代码可能在创建根页面时遗漏了draft_title参数,或者因为数据库结构不一致(比如之前迁移未完全执行),导致该字段被标记为非空但未被赋值。
3. 误操作迁移的影响
是的,如果你之前执行过python manage.py migrate(未指定--database),Django会把所有迁移(包括Wagtail的)应用到默认数据库,同时也会把你的自定义模型迁移应用到默认数据库。如果之后你又尝试对db_tool执行迁移,可能会导致:
- 默认数据库的Wagtail表结构处于异常状态(比如部分迁移执行,部分未执行)
db_tool数据库中意外创建了Wagtail的表,导致后续迁移时ORM操作混淆了数据库来源
排查与修复步骤
配置数据库路由:在
settings.py中添加路由规则,确保Wagtail模型只路由到默认数据库,自定义模型只路由到db_tool。示例配置:class WagtailRouter: def db_for_read(self, model, **hints): if model._meta.app_label in ['wagtailcore', 'wagtailadmin', 'wagtailimages', 'wagtaildocs']: return 'default' return None def db_for_write(self, model, **hints): if model._meta.app_label in ['wagtailcore', 'wagtailadmin', 'wagtailimages', 'wagtaildocs']: return 'default' return None def allow_migrate(self, db, app_label, model_name=None, **hints): if app_label in ['wagtailcore', 'wagtailadmin', 'wagtailimages', 'wagtaildocs']: return db == 'default' elif app_label == 'your_custom_app': return db == 'db_tool' return None将该路由类加入
settings.DATABASE_ROUTERS列表。检查并修复数据库表结构:
- 打开
db_tool.sqlite3,查看是否存在wagtailcore_page等Wagtail相关表,若存在则删除这些表,或直接重新初始化db_tool数据库。 - 打开
db.sqlite3,确认wagtailcore_page表的draft_title字段是否存在且为非空。若表结构异常,备份数据库后重新执行Wagtail迁移:python manage.py migrate wagtailcore --database default。
- 打开
规范迁移命令:
后续执行迁移时严格区分:- 自定义模型迁移:
python manage.py makemigrations your_custom_app,再执行python manage.py migrate --database db_tool - Wagtail相关迁移:
python manage.py migrate --database default
- 自定义模型迁移:
内容的提问来源于stack exchange,提问作者bripu

