Django(Wagtail CMS)迁移时IntegrityError约束检查失败求助
Wagtail CMS博客项目迁移与后台访问错误排查
基于Wagtail CMS官方教程搭建Django博客项目时,遇到两个核心问题:
- 执行
makemigrations和migrate命令时触发django.db.utils.IntegrityError: CHECK constraint failed: new__blog_blogpage错误 - 访问后台管理页面时出现
OperationalError,提示blog_blogindexpage.introduction列不存在
博客模型代码
from __future__ import unicode_literals from django import forms from django.db import models from modelcluster.contrib.taggit import ClusterTaggableManager from modelcluster.fields import ParentalKey, ParentalManyToManyField from wagtail.models import Page, Orderable from wagtail.fields import RichTextField, StreamField from wagtail.admin.panels import FieldPanel, InlinePanel, MultiFieldPanel, MultipleChooserPanel from wagtail.snippets.models import register_snippet from base.blocks import BaseStreamBlock # add this: from wagtail.search import index # keep the definition of BlogIndexPage model, and add the BlogPage model: class BlogPersonRelationship(Orderable, models.Model): page = ParentalKey( "BlogPage", related_name="blog_person_relationship", on_delete=models.CASCADE ) person = models.ForeignKey( "base.Person", related_name="person_blog_relationship", on_delete=models.CASCADE ) panels = [FieldPanel("person")] class BlogPage(Page): introduction = models.TextField(help_text="Text to describe the page.", blank=True) image = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", help_text="Landscape mode only; horizontal width between 1000px and 3000px.", ) body = StreamField( BaseStreamBlock(), verbose_name="Page body", blank=True, use_json_field=True ) subtitle = models.CharField(blank=True, max_length=255) # tags = ClusterTaggableManager(through=BlogPageTag, blank=True) CURRENTLY NOT USING TAGS date_published = models.DateField("Date article published", blank=True, null=True) content_panels = Page.content_panels + [ FieldPanel("subtitle"), FieldPanel("introduction"), FieldPanel("image"), FieldPanel("body"), FieldPanel("date_published"), MultipleChooserPanel( "blog_person_relationship", chooser_field_name="person", heading="Authors", label="Author", panels=None, min_num=1, ), # FieldPanel("tags"), ] search_fields = Page.search_fields + [ index.SearchField("body"), ] def authors(self): # only return authors that are not in draft return [ n.person for n in self.blog_person_relationship.filter( person__live=True ).select_related("person") ] # Specifies parent to BlogPage as being BlogIndexPages parent_page_types = ["BlogIndexPage"] # Specifies what content types can exist as children of BlogPage # Empty list means that no child content types are allowed subpage_types = [] class BlogIndexPage(Page): # intro = RichTextField(blank=True) # wagtail docs getting started tutorial introduction = models.TextField(help_text="Text to describe the page.", blank=True) image = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", help_text="Landscape mode only; horizontal width between 1000px and 3000px.", ) content_panels = Page.content_panels + [ FieldPanel("introduction"), FieldPanel("image"), ] # add the get_context method: def get_context(self, request): # update context to include only published posts, ordered by reverse-chron # blogpages = self.get_children().live().order_by('-first_published_at') # context['blogpages'] = blogpages context = super(BlogIndexPage, self).get_context(request) context["posts"] = ( BlogPage.objects.descendant_of(self).live().order_by("-date_published") ) return context def serve_preview(self, request, mode_name): # needed for previews to work return self.serve(request) # returns the child BlogPage objects for this BlogPageIndex # if a tag is used then it will filter the posts by tag def get_posts(self, tag=None): posts = BlogPage.objects.live().descendant_of(self) # if tag: # posts = posts.filter(tags=tag) return posts
错误详情1:后台访问错误
OperationalError at /admin/pages/4/ no such column: blog_blogindexpage.introduction Request Method: GET Request URL: http://127.0.0.1:8000/admin/pages/4/ Django Version: 4.2.11 Exception Type: OperationalError Exception Value: no such column: blog_blogindexpage.introduction Exception Location: /home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/backends/sqlite3/base.py, line 328, in execute Raised during: wagtail.admin.views.pages.listing.IndexView Python Executable: /home/david/projects/someproject/about/venv-about/bin/python3 Python Version: 3.8.10 Python Path: ['/home/david/projects/someproject/about/about', '/usr/lib/python38.zip', '/usr/lib/python3.8', '/usr/lib/python3.8/lib-dynload', '/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages'] Server time: Tue, 16 Apr 2024 18:18:24 +0000
错误详情2:迁移命令错误
Running migrations: Applying blog.0006_remove_blogindexpage_intro_remove_blogpage_authors_and_more...Traceback (most recent call last): File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/backends/utils.py", line 89, in _execute return self.cursor.execute(sql, params) File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/backends/sqlite3/base.py", line 328, in execute return super().execute(query, params) sqlite3.IntegrityError: CHECK constraint failed: new__blog_blogpage The above exception was the direct cause of the following exception: Traceback (most recent call last): File "manage.py", line 10, in <module> execute_from_command_line(sys.argv) File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line utility.execute() File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/core/management/__init__.py", line 436, in execute self.fetch_command(subcommand).run_from_argv(self.argv) File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/core/management/base.py", line 412, in run_from_argv self.execute(*args, **cmd_options) File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/core/management/base.py", line 458, in execute output = self.handle(*args, **options) File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/core/management/base.py", line 106, in wrapper res = handle_func(*args, **kwargs) File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/core/management/commands/migrate.py", line 356, in handle post_migrate_state = executor.migrate( File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/migrations/executor.py", line 135, in migrate state = self._migrate_all_forwards( File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/migrations/executor.py", line 167, in _migrate_all_forwards state = self.apply_migration( File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/migrations/executor.py", line 252, in apply_migration state = migration.apply(state, schema_editor) File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/migrations/migration.py", line 132, in apply operation.database_forwards( File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/migrations/operations/fields.py", line 235, in database_forwards schema_editor.alter_field(from_model, from_field, to_field) File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/backends/sqlite3/schema.py", line 175, in alter_field super().alter_field(model, old_field, new_field, strict=strict) File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/backends/base/schema.py", line 831, in alter_field self._alter_field( File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/backends/sqlite3/schema.py", line 463, in _alter_field self._remake_table(model, alter_fields=[(old_field, new_field)]) File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/backends/sqlite3/schema.py", line 336, in _remake_table self.execute( File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/backends/base/schema.py", line 201, in execute cursor.execute(sql, params) File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/backends/utils.py", line 102, in execute return super().execute(sql, params) File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/backends/utils.py", line 67, in execute return self._execute_with_wrappers( File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers return executor(sql, params, many, context) File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/backends/utils.py", line 89, in _execute return self.cursor.execute(sql, params) File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/utils.py", line 91, in __exit__ raise dj_exc_value.with_traceback(traceback) from exc_value File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/backends/utils.py", line 89, in _execute return self.cursor.execute(sql, params) File "/home/david/projects/someproject/about/venv-about/lib/python3.8/site-packages/django/db/backends/sqlite3/base.py", line 328, in execute return super().execute(query, params) django.db.utils.IntegrityError: CHECK constraint failed: new__blog_blogpage
问题分析与解决步骤
1. 错误根源
- 后台列不存在:修改
BlogIndexPage模型时,将原intro字段替换为introduction,但迁移未正确执行,导致数据库无对应字段。 - 迁移约束失败:SQLite修改表结构时会重建表,
CHECK constraint failed是因为现有数据违反新约束——BlogPage的MultipleChooserPanel设置了min_num=1,但已有博客页面未关联作者,触发非空约束。
2. 具体解决步骤
步骤1:临时移除严格约束
修改BlogPage中的MultipleChooserPanel,注释min_num=1:
MultipleChooserPanel( "blog_person_relationship", chooser_field_name="person", heading="Authors", label="Author", panels=None, # min_num=1, 临时注释此行 ),
步骤2:重置迁移文件
- 删除未执行的迁移文件(
blog/migrations/0006_*.py) - 重新生成迁移:
python manage.py makemigrations
- 打开新生成的迁移文件,确保逻辑为:先给
BlogIndexPage添加introduction字段,再移除旧的intro字段。
步骤3:执行迁移并补全数据
python manage.py migrate
迁移完成后,进入后台给所有已存在的BlogPage添加作者,再恢复min_num=1配置,重新生成并执行迁移。
步骤4:验证后台访问
重启服务后访问后台管理页面,确认OperationalError消失。
额外注意事项
- SQLite对表结构修改支持有限,复杂变更易出错,生产环境建议使用PostgreSQL。
- Wagtail页面模型修改后,必须确保迁移正确执行,避免模型与数据库结构不一致。
内容的提问来源于stack exchange,提问作者user3125823
相关产品推荐
相关产品推荐

