Коротка версія
НЕ слід використовувати 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),
]