精简版
您应不使用loaddata
的数据迁移直接管理命令。
# Bad example for a data migration
from django.db import migrations
from django.core.management import call_command
def load_fixture(apps, schema_editor):
# No, it's wrong. DON'T DO THIS!
call_command('loaddata', 'your_data.json', app_label='yourapp')
class Migration(migrations.Migration):
dependencies = [
# Dependencies to other migrations
]
operations = [
migrations.RunPython(load_fixture),
]
长版
loaddata
利用利用django.core.serializers.python.Deserializer
最新模型反序列化迁移中的历史数据。那是不正确的行为。
例如,假设有一个数据迁移,该数据迁移利用loaddata
管理命令从固定装置加载数据,并且该数据迁移已应用于您的开发环境。
以后,您决定将新的必填字段添加到相应的模型中,这样就可以对更新后的模型进行新迁移(并可能在./manage.py makemigrations
提示您时向新字段提供一次性值)。
您运行下一个迁移,一切顺利。
最后,开发完Django应用程序,然后将其部署在生产服务器上。现在是时候在生产环境上从头开始运行整个迁移了。
但是,数据迁移失败。这是因为来自loaddata
命令的反序列化模型(代表当前代码)无法与添加的新必填字段的空数据一起保存。原始灯具缺少必要的数据!
但是,即使使用新字段所需的数据更新了灯具,数据迁移仍然会失败。在运行数据迁移时,尚未应用将相应列添加到数据库的下一次迁移。您无法将数据保存到不存在的列中!
结论:在数据迁移中,该loaddata
命令引入了模型与数据库之间潜在的不一致。您绝对不应该在数据迁移中直接使用它。
解决方案
loaddata
命令依赖于django.core.serializers.python._get_model
功能以从固定装置中获取相应的模型,该装置将返回模型的最新版本。我们需要对其进行猴子修补,以便获得历史模型。
(以下代码适用于Django 1.8.x)
# Good example for a data migration
from django.db import migrations
from django.core.serializers import base, python
from django.core.management import call_command
def load_fixture(apps, schema_editor):
# Save the old _get_model() function
old_get_model = python._get_model
# Define new _get_model() function here, which utilizes the apps argument to
# get the historical version of a model. This piece of code is directly stolen
# from django.core.serializers.python._get_model, unchanged. However, here it
# has a different context, specifically, the apps variable.
def _get_model(model_identifier):
try:
return apps.get_model(model_identifier)
except (LookupError, TypeError):
raise base.DeserializationError("Invalid model identifier: '%s'" % model_identifier)
# Replace the _get_model() function on the module, so loaddata can utilize it.
python._get_model = _get_model
try:
# Call loaddata command
call_command('loaddata', 'your_data.json', app_label='yourapp')
finally:
# Restore old _get_model() function
python._get_model = old_get_model
class Migration(migrations.Migration):
dependencies = [
# Dependencies to other migrations
]
operations = [
migrations.RunPython(load_fixture),
]