Versi pendek
Anda TIDAK boleh menggunakan loaddata
perintah manajemen secara langsung dalam migrasi data.
# 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),
]
Versi panjang
loaddata
memanfaatkan django.core.serializers.python.Deserializer
yang menggunakan model paling mutakhir untuk deserialisasi data historis dalam migrasi. Itu perilaku yang salah.
Misalnya, ada migrasi data yang memanfaatkan loaddata
perintah manajemen untuk memuat data dari fixture, dan itu sudah diterapkan di lingkungan pengembangan Anda.
Kemudian, Anda memutuskan untuk menambahkan bidang baru yang diperlukan ke model yang sesuai, sehingga Anda melakukannya dan melakukan migrasi baru terhadap model yang diperbarui (dan mungkin memberikan nilai satu kali ke bidang baru saat./manage.py makemigrations
diminta).
Anda menjalankan migrasi berikutnya, dan semuanya baik-baik saja.
Akhirnya, Anda telah selesai mengembangkan aplikasi Django Anda, dan Anda menyebarkannya pada server produksi. Sekarang saatnya Anda menjalankan seluruh migrasi dari awal di lingkungan produksi.
Namun, migrasi data gagal . Itu karena model deserialized dari loaddata
perintah, yang mewakili kode saat ini, tidak dapat disimpan dengan data kosong untuk bidang baru yang diperlukan yang Anda tambahkan. Perlengkapan asli kekurangan data yang diperlukan untuk itu!
Tetapi bahkan jika Anda memperbarui fixture dengan data yang diperlukan untuk bidang baru, migrasi data masih gagal . Saat migrasi data berjalan, migrasi berikutnya yang menambahkan kolom terkait ke database, belum diterapkan. Anda tidak dapat menyimpan data ke kolom yang tidak ada!
Kesimpulan: dalam migrasi data,loaddata
perintah tersebut menyebabkan kemungkinan ketidakkonsistenan antara model dan database. Anda sebaiknya TIDAK menggunakannya secara langsung dalam migrasi data.
Solusinya
loaddata
perintah bergantung pada django.core.serializers.python._get_model
fungsi untuk mendapatkan model yang sesuai dari perlengkapan, yang akan mengembalikan versi model yang paling mutakhir. Kita perlu menambalnya sehingga mendapatkan model historis.
(Kode berikut bekerja untuk 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),
]