【发布时间】:2021-06-24 13:39:56
【问题描述】:
我一直在从 Wagtail v1.13 / Django v1.11 / Python 3.5.2 升级以更新项目。
一切都很顺利我现在在 Wagtail 2.10.2 / Django 3.0.14 / Python 3.8.9
该项目使用 puput,所以在 puput 支持更新版本的 Django 之前,不能超越 Django 3.0.14。这不是问题,我现在对此很满意。
但是在安装 Wagtail 2.11.8 后,当我运行 makemigrations 时,我收到以下错误:
AttributeError: module 'wagtail.core.models' has no attribute 'MultiTableCopyMixin'
Wagtail 2.10.2/Django 3.0.14 组合创建了 0038_auto_20210624_0634.py,其中包含以下行:
bases=(wagtail.core.models.MultiTableCopyMixin, models.Model),
Wagtail 2.10.2 中果然存在 wagtail.core.models.MultiTableCopyMixin,但是在 Wagtail 2.11.* MultiTableCopyMixin 已经被移除了。
我也尝试将 Django 保持在 2.2.24 版本,但仍然遇到同样的问题。
有什么想法吗?
下面自动生成的迁移的完整内容,请参见操作数组的第二个条目:
# Generated by Django 2.2.24 on 2021-06-24 14:07
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import modelcluster.fields
import wagtail.core.models
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
('auth', '0011_update_proxy_permissions'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('wagtailcore', '0037_auto_20210624_0316'),
]
operations = [
migrations.CreateModel(
name='Task',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, verbose_name='name')),
('active', models.BooleanField(default=True, help_text='Active tasks can be added to workflows. Deactivating a task does not remove it from existing workflows.', verbose_name='active')),
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='wagtail_tasks', to='contenttypes.ContentType', verbose_name='content type')),
],
options={
'verbose_name': 'task',
'verbose_name_plural': 'tasks',
},
),
migrations.CreateModel(
name='TaskState',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.CharField(choices=[('in_progress', 'In progress'), ('approved', 'Approved'), ('rejected', 'Rejected'), ('skipped', 'Skipped'), ('cancelled', 'Cancelled')], default='in_progress', max_length=50, verbose_name='status')),
('started_at', models.DateTimeField(auto_now_add=True, verbose_name='started at')),
('finished_at', models.DateTimeField(blank=True, null=True, verbose_name='finished at')),
('comment', models.TextField(blank=True)),
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='wagtail_task_states', to='contenttypes.ContentType', verbose_name='content type')),
('finished_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='finished_task_states', to=settings.AUTH_USER_MODEL, verbose_name='finished by')),
('page_revision', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='task_states', to='wagtailcore.PageRevision', verbose_name='page revision')),
('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='task_states', to='wagtailcore.Task', verbose_name='task')),
],
options={
'verbose_name': 'Task state',
'verbose_name_plural': 'Task states',
},
bases=(wagtail.core.models.MultiTableCopyMixin, models.Model),
),
migrations.CreateModel(
name='Workflow',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, verbose_name='name')),
('active', models.BooleanField(default=True, help_text='Active workflows can be added to pages. Deactivating a workflow does not remove it from existing pages.', verbose_name='active')),
],
options={
'verbose_name': 'workflow',
'verbose_name_plural': 'workflows',
},
),
migrations.AlterField(
model_name='site',
name='site_name',
field=models.CharField(blank=True, default='Default', help_text='Human-readable name for the site.', max_length=255, verbose_name='site name'),
preserve_default=False,
),
migrations.CreateModel(
name='GroupApprovalTask',
fields=[
('task_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Task')),
],
options={
'verbose_name': 'Group approval task',
'verbose_name_plural': 'Group approval tasks',
},
bases=('wagtailcore.task',),
),
migrations.CreateModel(
name='WorkflowState',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.CharField(choices=[('in_progress', 'In progress'), ('approved', 'Approved'), ('needs_changes', 'Needs changes'), ('cancelled', 'Cancelled')], default='in_progress', max_length=50, verbose_name='status')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='created at')),
('current_task_state', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='wagtailcore.TaskState', verbose_name='current task state')),
('page', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workflow_states', to='wagtailcore.Page', verbose_name='page')),
('requested_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='requested_workflows', to=settings.AUTH_USER_MODEL, verbose_name='requested by')),
('workflow', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workflow_states', to='wagtailcore.Workflow', verbose_name='workflow')),
],
options={
'verbose_name': 'Workflow state',
'verbose_name_plural': 'Workflow states',
},
),
migrations.CreateModel(
name='WorkflowPage',
fields=[
('page', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, serialize=False, to='wagtailcore.Page', verbose_name='page')),
('workflow', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workflow_pages', to='wagtailcore.Workflow', verbose_name='workflow')),
],
options={
'verbose_name': 'workflow page',
'verbose_name_plural': 'workflow pages',
},
),
migrations.AddField(
model_name='taskstate',
name='workflow_state',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='task_states', to='wagtailcore.WorkflowState', verbose_name='workflow state'),
),
migrations.CreateModel(
name='PageLogEntry',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('label', models.TextField()),
('action', models.CharField(blank=True, db_index=True, max_length=255)),
('data_json', models.TextField(blank=True)),
('timestamp', models.DateTimeField(verbose_name='timestamp (UTC)')),
('content_changed', models.BooleanField(db_index=True, default=False)),
('deleted', models.BooleanField(default=False)),
('content_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='contenttypes.ContentType', verbose_name='content type')),
('page', models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='wagtailcore.Page')),
('revision', models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='wagtailcore.PageRevision')),
('user', models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'page log entry',
'verbose_name_plural': 'page log entries',
'ordering': ['-timestamp', '-id'],
},
),
migrations.CreateModel(
name='WorkflowTask',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sort_order', models.IntegerField(blank=True, editable=False, null=True)),
('task', models.ForeignKey(limit_choices_to={'active': True}, on_delete=django.db.models.deletion.CASCADE, related_name='workflow_tasks', to='wagtailcore.Task', verbose_name='task')),
('workflow', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='workflow_tasks', to='wagtailcore.Workflow', verbose_name='workflow_tasks')),
],
options={
'verbose_name': 'workflow task order',
'verbose_name_plural': 'workflow task orders',
'ordering': ['sort_order'],
'abstract': False,
'unique_together': {('workflow', 'task')},
},
),
migrations.AddConstraint(
model_name='workflowstate',
constraint=models.UniqueConstraint(condition=models.Q(('status', 'in_progress'), ('status', 'needs_changes'), _connector='OR'), fields=('page',), name='unique_in_progress_workflow'),
),
migrations.AddField(
model_name='groupapprovaltask',
name='groups',
field=models.ManyToManyField(help_text='Pages at this step in a workflow will be moderated or approved by these groups of users', to='auth.Group', verbose_name='groups'),
),
]
showmigrations 的输出如下,在 Wagtail 2.10.2 上运行,尝试在 2.11.8 上运行会抛出与之前相同的错误:
admin
[X] 0001_initial
[X] 0002_logentry_remove_auto_add
[X] 0003_logentry_add_action_flag_choices
auth
[X] 0001_initial
[X] 0002_alter_permission_name_max_length
[X] 0003_alter_user_email_max_length
[X] 0004_alter_user_username_opts
[X] 0005_alter_user_last_login_null
[X] 0006_require_contenttypes_0002
[X] 0007_alter_validators_add_error_messages
[X] 0008_alter_user_username_max_length
[X] 0009_alter_user_last_name_max_length
[X] 0010_alter_group_name_max_length
[X] 0011_update_proxy_permissions
contenttypes
[X] 0001_initial
[X] 0002_remove_content_type_name
home
[X] 0001_initial
[X] 0002_contentpage
[X] 0003_auto_20170427_1205
[X] 0004_auto_20170427_1210
[X] 0005_auto_20170427_1214
[X] 0006_auto_20170427_1215
[X] 0007_auto_20170427_1216
[X] 0008_auto_20170430_0530
[X] 0009_auto_20170430_0551
[X] 0010_auto_20170430_0639
[X] 0011_auto_20170430_0642
[X] 0012_auto_20170501_1035
[X] 0013_auto_20170501_1046
[X] 0014_auto_20170501_1055
[X] 0015_auto_20170501_1101
[X] 0016_auto_20170501_1103
[X] 0017_auto_20170501_1106
[X] 0018_auto_20170501_2305
[X] 0019_auto_20170501_2325
[X] 0020_auto_20170501_2350
[X] 0021_auto_20170501_2354
[X] 0022_auto_20170502_0000
[X] 0023_auto_20170502_0012
[X] 0024_auto_20170502_0030
[X] 0025_auto_20170502_0054
[X] 0026_auto_20170502_0237
[X] 0027_auto_20170502_0524
[X] 0028_auto_20170502_0853
[X] 0029_auto_20170502_0856
[X] 0030_auto_20170511_0012
[X] 0031_auto_20170511_0017
[X] 0032_auto_20170511_0021
[X] 0033_auto_20170511_0023
[X] 0034_auto_20170511_0025
[X] 0035_auto_20170511_0027
[X] 0036_auto_20170511_0030
[X] 0037_auto_20170511_0032
[X] 0038_auto_20170511_0034
[X] 0039_auto_20170511_0035
[X] 0040_auto_20170511_0037
[X] 0041_auto_20170511_0039
[X] 0042_auto_20170511_0045
[X] 0043_auto_20170511_0048
[X] 0044_auto_20170511_0054
[X] 0045_auto_20170511_0056
[X] 0046_auto_20170511_0105
[X] 0047_auto_20170511_0106
[X] 0048_auto_20170511_0111
[X] 0049_auto_20170511_0113
[X] 0050_auto_20170511_0114
[X] 0051_auto_20170513_0708
[X] 0052_auto_20170513_0709
[X] 0053_auto_20170515_2351
[X] 0054_auto_20170516_0021
[X] 0055_auto_20170516_0344
[X] 0056_contactpage
[X] 0057_auto_20170719_1137
[X] 0058_auto_20170824_0004
[X] 0059_auto_20170824_0014
[X] 0060_auto_20171016_0146
[X] 0061_auto_20171120_0657
[X] 0062_auto_20171120_0709
[X] 0063_auto_20171120_0711
[X] 0064_auto_20171120_0719
[X] 0065_auto_20171120_0720
[X] 0066_auto_20171120_0725
[X] 0067_auto_20171126_0205
[X] 0068_auto_20171127_2239
[X] 0069_auto_20171207_0317
[X] 0070_auto_20180122_0204
[X] 0071_auto_20180204_2258
[X] 0072_auto_20180204_2306
[X] 0073_donatepage
[X] 0074_contact2page
[X] 0075_auto_20180705_0128
[X] 0076_auto_20190926_1406
[X] 0077_auto_20190926_1557
[X] 0078_auto_20191001_0448
[X] 0079_auto_20191003_1227
[X] 0080_auto_20200211_1115
[X] 0081_auto_20210217_1427
[X] 0082_contentholdercategory_contentholderdetailpage_contentholderlistingpage
[X] 0083_contentholderdetailpage_show_categories
[X] 0084_auto_20210310_0455
[X] 0085_contentholderlistingpage_body2
[X] 0086_flexiblecontactpage
[X] 0087_auto_20210501_0155
[X] 0088_auto_20210503_1053
[X] 0089_auto_20210503_1123
[X] 0090_auto_20210511_1644
[X] 0091_auto_20210511_1656
[X] 0092_auto_20210519_1637
[X] 0093_auto_20210525_0709
[X] 0094_footersettings
[X] 0095_auto_20210607_1158
[X] 0096_auto_20210623_0242
leads
[X] 0001_initial
[X] 0002_auto_20170524_0535
[X] 0003_auto_20170525_0041
[X] 0004_auto_20170525_0131
[X] 0005_auto_20170525_0142
[X] 0006_auto_20170525_0326
[X] 0007_leaddb_referer
[X] 0008_auto_20170525_0851
[X] 0009_auto_20170526_0230
[X] 0010_auto_20170526_0232
[X] 0011_auto_20170526_0313
[X] 0012_auto_20170526_0321
[X] 0013_auto_20170526_0324
[X] 0014_auto_20180322_0049
[X] 0015_auto_20180713_0351
[X] 0016_auto_20180713_0357
[X] 0017_auto_20180713_0708
[X] 0018_auto_20180713_0715
[X] 0019_auto_20180713_0754
[X] 0020_auto_20180713_0803
[X] 0021_auto_20180713_0804
[X] 0022_leaddb_other
[X] 0023_auto_20180720_0341
[X] 0024_auto_20180724_0224
[X] 0025_auto_20190717_1540
[X] 0026_auto_20190717_1616
[X] 0027_auto_20190717_1618
[X] 0028_auto_20190718_1533
[X] 0029_auto_20190721_1146
[X] 0030_auto_20190924_1501
[X] 0031_auto_20210217_1427
[X] 0032_flexibleleaddb
[X] 0033_auto_20210519_1637
[X] 0034_auto_20210621_1407
[X] 0035_auto_20210621_1534
postgres_search
[X] 0001_initial
[X] 0002_add_autocomplete
[X] 0003_title
puput
[X] 0001_initial
[X] 0002_blogpage_short_feed_description
[X] 0003_remove_blogpage_short_feed_description
[X] 0004_blogpage_short_feed_description
[X] 0005_auto_20210620_1525
sessions
[X] 0001_initial
sites
[X] 0001_initial
[X] 0002_alter_domain_unique
taggit
[X] 0001_initial
[X] 0002_auto_20150616_2121
[X] 0003_taggeditem_add_unique_index
wagtailadmin
[X] 0001_create_admin_access_permissions
wagtailcore
[X] 0001_squashed_0016_change_page_url_path_to_text_field (16 squashed migrations)
[X] 0017_change_edit_page_permission_description
[X] 0018_pagerevision_submitted_for_moderation_index
[X] 0019_verbose_names_cleanup
[X] 0020_add_index_on_page_first_published_at
[X] 0021_capitalizeverbose
[X] 0022_add_site_name
[X] 0023_alter_page_revision_on_delete_behaviour
[X] 0024_collection
[X] 0025_collection_initial_data
[X] 0026_group_collection_permission
[X] 0027_fix_collection_path_collation
[X] 0024_alter_page_content_type_on_delete_behaviour
[X] 0028_merge
[X] 0029_unicode_slugfield_dj19
[X] 0030_index_on_pagerevision_created_at
[X] 0031_add_page_view_restriction_types
[X] 0032_add_bulk_delete_page_permission
[X] 0033_auto_20170401_0651
[X] 0034_auto_20170510_0533
[X] 0035_auto_20170822_1810
[X] 0036_auto_20210624_0046
[X] 0037_auto_20210624_0316
[X] 0038_auto_20210624_0634
wagtaildocs
[X] 0001_initial
[X] 0002_initial_data
[X] 0003_add_verbose_names
[X] 0004_capitalizeverbose
[X] 0005_document_collection
[X] 0006_copy_document_permissions_to_collections
[X] 0005_alter_uploaded_by_user_on_delete_action
[X] 0007_merge
[X] 0008_document_file_size
[X] 0009_document_verbose_name_plural
[X] 0010_document_file_hash
wagtailembeds
[X] 0001_initial
[X] 0002_add_verbose_names
[X] 0003_capitalizeverbose
[X] 0004_embed_verbose_name_plural
[X] 0005_specify_thumbnail_url_max_length
wagtailforms
[X] 0001_initial
[X] 0002_add_verbose_names
[X] 0003_capitalizeverbose
[X] 0004_add_verbose_name_plural
wagtailimages
[X] 0001_squashed_0021 (21 squashed migrations)
[X] 0022_uploadedimage
wagtailmenus
[X] 0001_initial
[X] 0002_auto_20160129_0901
[X] 0003_auto_20160129_2339
[X] 0004_auto_20160130_0024
[X] 0005_auto_20160130_2209
[X] 0006_auto_20160131_1347
[X] 0007_auto_20160131_2000
[X] 0008_auto_20160131_2327
[X] 0009_auto_20160201_0859
[X] 0010_auto_20160201_1558
[X] 0011_auto_20160415_1519
[X] 0012_auto_20160419_0039
[X] 0013_auto_20160423_1124
[X] 0014_auto_20160423_1339
[X] 0015_auto_20160423_1348
[X] 0016_auto_20160930_0645
[X] 0017_auto_20161013_1658
[X] 0018_auto_20161204_2043
[X] 0019_auto_20161204_2143
[X] 0020_auto_20161210_0004
[X] 0021_auto_20170106_2352
[X] 0022_auto_20170913_2125
[X] 0023_remove_use_specific
wagtailmetadata
[X] 0001_initial
[X] 0002_auto_20160607_2150
[X] 0003_auto_20160909_1000
[X] 0004_remove_sitemetadatapreferences
[X] 0005_metadatasettings
[X] 0006_drop_metadatasettings
wagtailredirects
[X] 0001_initial
[X] 0002_add_verbose_names
[X] 0003_make_site_field_editable
[X] 0004_set_unique_on_path_and_site
[X] 0005_capitalizeverbose
[X] 0006_redirect_increase_max_length
wagtailsearch
[X] 0001_initial
[X] 0002_add_verbose_names
[X] 0003_remove_editors_pick
[X] 0004_querydailyhits_verbose_name_plural
wagtailusers
[X] 0001_initial
[X] 0002_add_verbose_name_on_userprofile
[X] 0003_add_verbose_names
[X] 0004_capitalizeverbose
[X] 0005_make_related_name_wagtail_specific
[X] 0006_userprofile_prefered_language
[X] 0007_userprofile_current_time_zone
[X] 0008_userprofile_avatar
[X] 0009_userprofile_verbose_name_plural
在看到最新版本的 wagtail 没有 'bases' 行后,我发现如果我在添加到我的项目中的 wagtailcore_migrations 中注释掉 bases 行,错误就会消失。
但我随后被问到:
You are trying to add a non-nullable field 'locale' to page without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
当我选择 1 时,我得到:
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
我尝试输入“en-us”和 timezone.now。但是我在运行 migrate 时总是出错:
TypeError: Field 'id' expected a number but got...
后跟
- 当我输入'en-us'时'en-us'
- datetime.datetime 当我进入 timezone.now 时
我在这里放什么?
我认为它不是在创建 Locale 数据库表。
我注意到它在 home/wagtailcore_migrations 中创建文件,这些文件主要复制 site-packages/wagtail/core/migrations 中的文件。怎么会这样?
【问题讨论】:
-
您能否分享完整的
0038_auto_20210624_0634.py(或至少有足够的上下文来查看bases选项定义在哪个类/模型上)? -
按要求添加了完整的 0038_auto_20210624_0634.py。
-
啊 - 这是在 wagtailcore 内部创建的迁移?这不应该发生——除了 Wagtail 包本身包含的迁移之外,wagtailcore 永远不应该进行迁移,否则您将遇到迁移历史的一致性问题。我怀疑这是一个 Puput 错误...
-
是的,从 wagtail 核心内部自动生成。我检查过,它来自 /wagtail/core/migrations/0047_add_workflow_models.py 中的 wagtail 分布。
-
我不确定您是如何最终得到一个与 the same table creations as Wagtail's own migrations 重复的自动生成迁移,除非您之前曾使用过该迁移文件夹来解决早期的迁移冲突 - 其中如果问题可以追溯到更远的地方,这可能会变得非常棘手。你能分享一下运行
./manage.py showmigrations的输出吗?