loaddata 在迁移中调用时引发 IntegrityError,但如果从 CLI 调用则有效
loaddata raises IntegrityError when called within migration, but works if called from the CLI
对于我的 Django 1.8.12 站点,我有一个 example_content
固定装置,用于填充一些初始站点内容。
如果我在 运行ning migrate
之后立即加载它,它可以正常导入:
$ ./manage.py migrate --noinput
Operations to perform:
Synchronize unmigrated apps: ckeditor, staticfiles, zinnia_ckeditor, messages, djangocms_admin_style, webapp_creator, template_debug, sekizai, django_pygments, treebeard
Apply all migrations: djangocms_inherit, djangocms_snippet, api_docs, cmsplugin_zinnia, sites, menus, contenttypes, store_data, zinnia, django_comments, sessions, rev
ersion, auth, djangocms_picture, tagging, developer_portal, admin, djangocms_link, djangocms_video, django_openid_auth, md_importer, cms, djangocms_text_ckeditor
Synchronizing apps without migrations:
Creating tables...
Running deferred SQL...
Installing custom SQL...
Running migrations:
Rendering model states... DONE
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
... (skipped for brevity).
Applying django_comments.0003_add_submit_date_index... OK
$ ./manage.py loaddata example_content
Installed 139582 object(s) from 1 fixture(s)
$
但是,我希望这些数据在 migrate
上自动导入,而不是需要额外的手动 loaddata
步骤,所以我正在尝试创建一个迁移,my_app.0002_example_content
, 调用 loaddata
命令。
我通过在每个相关模块上添加依赖项来确保首先加载所有其他数据。
为了确认此迁移确实是 运行 最后一次(就像上面成功时一样),我首先注释掉了 RunPython
步骤,将迁移留空:
# my_app/migrations/0002_example_content.py
from django.core.management import call_command
from django.db import models, migrations
class Migration(migrations.Migration):
from django.core.management import call_command
from django.db import models, migrations
dependencies = [
('djangocms_inherit', '0002_auto_20150622_1244'),
... all other apps (skipped for brevity)
('my_app', '0001_initial'),
]
operations = [
# migrations.RunPython(
# lambda apps, schema_editor: call_command("loaddata", "example_content")
# )
]
果然 运行 是最后一次迁移:
$ ./manage.py migrate --noinput
Operations to perform:
Synchronize unmigrated apps: ckeditor, staticfiles, zinnia_ckeditor, messages, djangocms_admin_style, webapp_creator, template_debug, sekizai, django_pygments, treebeard
Apply all migrations: djangocms_inherit, djangocms_snippet, api_docs, cmsplugin_zinnia, sites, menus, contenttypes, store_data, zinnia, django_comments, sessions, reversion, auth, djangocms_picture, tagging, developer_portal, admin, djangocms_link, djangocms_video, django_openid_auth, md_importer, cms, djangocms_text_ckeditor
Synchronizing apps without migrations:
Creating tables...
Running deferred SQL...
Installing custom SQL...
Running migrations:
Rendering model states... DONE
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
... (skipped for brevity)
Applying django_comments.0003_add_submit_date_index... OK
Applying my_app.0001_initial... OK
Applying my_app.0002_example_content... OK
$
然而,当我尝试 运行 它实际上 运行 loaddata
命令(重新注释代码)时,我得到一个错误:
$ ./manage.py migrate --noinput
Operations to perform:
Synchronize unmigrated apps: ckeditor, staticfiles, zinnia_ckeditor, messages, djangocms_admin_style, webapp_creator, template_debug, sekizai, django_pygments, treebeard
Apply all migrations: djangocms_inherit, djangocms_snippet, api_docs, cmsplugin_zinnia, sites, menus, contenttypes, store_data, zinnia, django_comments, sessions, reversion, auth, djangocms_picture, tagging, developer_portal, admin, djangocms_link, djangocms_video, django_openid_auth, md_importer, cms, djangocms_text_ckeditor
Synchronizing apps without migrations:
Creating tables...
Running deferred SQL...
Installing custom SQL...
Running migrations:
Rendering model states... DONE
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
... (skipped for brevity)
Applying django_comments.0003_add_submit_date_index... OK
Applying my_app.0001_initial... OK
Applying my_app.0002_example_content...Traceback (most recent call last):
...
django.db.utils.IntegrityError: Problem installing fixtures: The row in table 'django_comments' with primary key '1' has an invalid foreign key: django_comments.content
_type_id contains a value '37' that does not have a corresponding value in django_content_type.id.
鉴于我的新迁移肯定是 运行 之后的一切,顺序应该与我手动调用 loaddata
时的顺序完全相同。所以我不明白为什么它在通过 CLI 调用时会成功,但在通过迁移调用时会失败。
在 migrate
步骤结束时是否发生了我遗漏的事情,这可能解释了这里的差异?
Django 使用 post_migrate
信号来创建缺失的内容类型,因此如果这是您第一次在当前数据库上迁移,则内容类型将不存在,直到加载数据的迁移具有 运行.
您可以手动创建迁移中需要的内容类型。 Django 将检测现有的内容类型,并且不会创建任何重复项。
请注意,内容类型的特定 ID 可能因数据库而异。使用 natural foreign keys 转储数据可能更好。
在 的提示下,我设法解决了这个问题。
最初我尝试手动调用 update_contenttypes
, but that on its own didn't work. It turns out I need to actually call all the post_migrate
信号,因为我猜他们会做更多需要的整理工作。
这是我最后得到的my_app.0002_example_content
:
# my_app/migrations/0002_example_content.py
from django.core.management import call_command
from django.db import migrations
from django.apps import apps
from django.db.models.signals import post_migrate
def finish_previous_migrations(migrate_apps):
"""
Explicitly run the post_migrate actions
for all apps
"""
for app_config in apps.get_app_configs():
post_migrate.send(
sender=app_config,
app_config=app_config
)
def load_site_content(migrate_apps, schema_editor):
"""
Load the website content fixture
"""
finish_previous_migrations(migrate_apps)
call_command("loaddata", "example_content")
class Migration(migrations.Migration):
dependencies = [
('djangocms_inherit', '0002_auto_20150622_1244'),
... all other apps (skipped for brevity)
('my_app', '0001_initial'),
]
operations = [
migrations.RunPython(load_site_content)
]
对于我的 Django 1.8.12 站点,我有一个 example_content
固定装置,用于填充一些初始站点内容。
如果我在 运行ning migrate
之后立即加载它,它可以正常导入:
$ ./manage.py migrate --noinput
Operations to perform:
Synchronize unmigrated apps: ckeditor, staticfiles, zinnia_ckeditor, messages, djangocms_admin_style, webapp_creator, template_debug, sekizai, django_pygments, treebeard
Apply all migrations: djangocms_inherit, djangocms_snippet, api_docs, cmsplugin_zinnia, sites, menus, contenttypes, store_data, zinnia, django_comments, sessions, rev
ersion, auth, djangocms_picture, tagging, developer_portal, admin, djangocms_link, djangocms_video, django_openid_auth, md_importer, cms, djangocms_text_ckeditor
Synchronizing apps without migrations:
Creating tables...
Running deferred SQL...
Installing custom SQL...
Running migrations:
Rendering model states... DONE
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
... (skipped for brevity).
Applying django_comments.0003_add_submit_date_index... OK
$ ./manage.py loaddata example_content
Installed 139582 object(s) from 1 fixture(s)
$
但是,我希望这些数据在 migrate
上自动导入,而不是需要额外的手动 loaddata
步骤,所以我正在尝试创建一个迁移,my_app.0002_example_content
, 调用 loaddata
命令。
我通过在每个相关模块上添加依赖项来确保首先加载所有其他数据。
为了确认此迁移确实是 运行 最后一次(就像上面成功时一样),我首先注释掉了 RunPython
步骤,将迁移留空:
# my_app/migrations/0002_example_content.py
from django.core.management import call_command
from django.db import models, migrations
class Migration(migrations.Migration):
from django.core.management import call_command
from django.db import models, migrations
dependencies = [
('djangocms_inherit', '0002_auto_20150622_1244'),
... all other apps (skipped for brevity)
('my_app', '0001_initial'),
]
operations = [
# migrations.RunPython(
# lambda apps, schema_editor: call_command("loaddata", "example_content")
# )
]
果然 运行 是最后一次迁移:
$ ./manage.py migrate --noinput
Operations to perform:
Synchronize unmigrated apps: ckeditor, staticfiles, zinnia_ckeditor, messages, djangocms_admin_style, webapp_creator, template_debug, sekizai, django_pygments, treebeard
Apply all migrations: djangocms_inherit, djangocms_snippet, api_docs, cmsplugin_zinnia, sites, menus, contenttypes, store_data, zinnia, django_comments, sessions, reversion, auth, djangocms_picture, tagging, developer_portal, admin, djangocms_link, djangocms_video, django_openid_auth, md_importer, cms, djangocms_text_ckeditor
Synchronizing apps without migrations:
Creating tables...
Running deferred SQL...
Installing custom SQL...
Running migrations:
Rendering model states... DONE
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
... (skipped for brevity)
Applying django_comments.0003_add_submit_date_index... OK
Applying my_app.0001_initial... OK
Applying my_app.0002_example_content... OK
$
然而,当我尝试 运行 它实际上 运行 loaddata
命令(重新注释代码)时,我得到一个错误:
$ ./manage.py migrate --noinput
Operations to perform:
Synchronize unmigrated apps: ckeditor, staticfiles, zinnia_ckeditor, messages, djangocms_admin_style, webapp_creator, template_debug, sekizai, django_pygments, treebeard
Apply all migrations: djangocms_inherit, djangocms_snippet, api_docs, cmsplugin_zinnia, sites, menus, contenttypes, store_data, zinnia, django_comments, sessions, reversion, auth, djangocms_picture, tagging, developer_portal, admin, djangocms_link, djangocms_video, django_openid_auth, md_importer, cms, djangocms_text_ckeditor
Synchronizing apps without migrations:
Creating tables...
Running deferred SQL...
Installing custom SQL...
Running migrations:
Rendering model states... DONE
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
... (skipped for brevity)
Applying django_comments.0003_add_submit_date_index... OK
Applying my_app.0001_initial... OK
Applying my_app.0002_example_content...Traceback (most recent call last):
...
django.db.utils.IntegrityError: Problem installing fixtures: The row in table 'django_comments' with primary key '1' has an invalid foreign key: django_comments.content
_type_id contains a value '37' that does not have a corresponding value in django_content_type.id.
鉴于我的新迁移肯定是 运行 之后的一切,顺序应该与我手动调用 loaddata
时的顺序完全相同。所以我不明白为什么它在通过 CLI 调用时会成功,但在通过迁移调用时会失败。
在 migrate
步骤结束时是否发生了我遗漏的事情,这可能解释了这里的差异?
Django 使用 post_migrate
信号来创建缺失的内容类型,因此如果这是您第一次在当前数据库上迁移,则内容类型将不存在,直到加载数据的迁移具有 运行.
您可以手动创建迁移中需要的内容类型。 Django 将检测现有的内容类型,并且不会创建任何重复项。
请注意,内容类型的特定 ID 可能因数据库而异。使用 natural foreign keys 转储数据可能更好。
在
最初我尝试手动调用 update_contenttypes
, but that on its own didn't work. It turns out I need to actually call all the post_migrate
信号,因为我猜他们会做更多需要的整理工作。
这是我最后得到的my_app.0002_example_content
:
# my_app/migrations/0002_example_content.py
from django.core.management import call_command
from django.db import migrations
from django.apps import apps
from django.db.models.signals import post_migrate
def finish_previous_migrations(migrate_apps):
"""
Explicitly run the post_migrate actions
for all apps
"""
for app_config in apps.get_app_configs():
post_migrate.send(
sender=app_config,
app_config=app_config
)
def load_site_content(migrate_apps, schema_editor):
"""
Load the website content fixture
"""
finish_previous_migrations(migrate_apps)
call_command("loaddata", "example_content")
class Migration(migrations.Migration):
dependencies = [
('djangocms_inherit', '0002_auto_20150622_1244'),
... all other apps (skipped for brevity)
('my_app', '0001_initial'),
]
operations = [
migrations.RunPython(load_site_content)
]