Saya baru-baru ini beralih dari Django 1.6 ke 1.7, dan saya mulai menggunakan migrasi (saya tidak pernah menggunakan Selatan).
Sebelum 1.7, saya biasa memuat data awal dengan fixture/initial_data.json
file, yang dimuat dengan python manage.py syncdb
perintah (saat membuat database).
Sekarang, saya mulai menggunakan migrasi, dan perilaku ini tidak digunakan lagi:
Jika aplikasi menggunakan migrasi, tidak ada pemuatan perlengkapan secara otomatis. Sejak migrasi akan dibutuhkan untuk aplikasi di Django 2.0, perilaku ini dianggap ditinggalkan. Jika Anda ingin memuat data awal untuk suatu aplikasi, pertimbangkan untuk melakukannya dalam migrasi data. ( https://docs.djangoproject.com/en/1.7/howto/initial-data/#automatically-loading-initial-data-fixtures )
The dokumentasi resmi tidak memiliki contoh yang jelas tentang bagaimana untuk melakukannya, jadi pertanyaan saya adalah:
Apa cara terbaik untuk mengimpor data awal seperti itu menggunakan migrasi data:
- Tulis kode Python dengan banyak panggilan ke
mymodel.create(...)
, - Gunakan atau tulis fungsi Django ( seperti memanggil
loaddata
) untuk memuat data dari file perlengkapan JSON.
Saya lebih suka opsi kedua.
Saya tidak ingin menggunakan Selatan, karena Django tampaknya dapat melakukannya secara asli sekarang.
Jawaban:
Pembaruan : Lihat komentar @ GwynBleidD di bawah untuk masalah yang dapat ditimbulkan oleh solusi ini, dan lihat jawaban @Rockallite di bawah ini untuk pendekatan yang lebih tahan lama untuk perubahan model di masa mendatang.
Dengan asumsi Anda memiliki file fixture di
<yourapp>/fixtures/initial_data.json
Buat migrasi kosong Anda:
Di Django 1.7:
Di Django 1.8+, Anda dapat memberikan nama:
Edit file migrasi Anda
<yourapp>/migrations/0002_auto_xxx.py
2.1. Implementasi kustom, terinspirasi oleh Django '
loaddata
(jawaban awal):2.2. Solusi yang lebih sederhana untuk
load_fixture
(sesuai saran @ juliocesar):Berguna jika Anda ingin menggunakan direktori kustom.
2.3. Paling sederhana: memanggil
loaddata
denganapp_label
akan memuat perlengkapan dari direktori<yourapp>
'sfixtures
secara otomatis:Jika Anda tidak menentukan
app_label
, loaddata akan mencoba memuatfixture
nama file dari semua direktori perlengkapan aplikasi (yang mungkin tidak Anda inginkan).Menjalankannya
sumber
loaddata('loaddata', fixture_filename, app_label='<yourapp>')
juga akan langsung menuju ke direktori perlengkapan aplikasi (karenanya tidak perlu membangun jalur lengkap perlengkapan itu)models.py
file saat ini , yang dapat memiliki beberapa bidang tambahan atau beberapa perubahan lainnya. Jika beberapa perubahan dibuat setelah membuat migrasi, itu akan gagal (jadi kami bahkan tidak dapat membuat migrasi skema setelah migrasi itu). Untuk mengatasinya, kita tidak dapat mengubah registri aplikasi yang sedang dikerjakan serializer ke registri yang disediakan untuk fungsi migrasi pada parameter pertama. Registri ke jalur terletak didjango.core.serializers.python.apps
.app registry
, tanpa mengubah variabel global (yang dapat menyebabkan masalah di masa depan hipotetis dengan migrasi database paralel).Versi pendek
Anda TIDAK boleh menggunakan
loaddata
perintah manajemen secara langsung dalam migrasi data.Versi panjang
loaddata
memanfaatkandjango.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 padadjango.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)
sumber
objects = serializers.deserialize('json', fixture, ignorenonexistent=True)
mengalami masalah yang sama sepertiloaddata
? Atau apakahignorenonexistent=True
mencakup semua kemungkinan masalah?ignorenonexistent=True
argumen memiliki dua efek: 1) mengabaikan model perlengkapan yang tidak ada dalam definisi model terbaru, 2) mengabaikan bidang model perlengkapan yang tidak dalam definisi model terkait terbaru. Tak satu pun dari mereka menangani situasi baru-bidang-dalam-model . Jadi, ya, saya pikir itu mengalami masalah yang sama seperti biasaloaddata
.natural_key()
, yang tampaknya tidak didukung oleh metode ini - Saya baru saja mengganti nilai natural_key dengan id sebenarnya dari model yang direferensikan.Terinspirasi oleh beberapa komentar (yaitu n__o) dan fakta bahwa saya memiliki banyak
initial_data.*
file yang tersebar di beberapa aplikasi saya memutuskan untuk membuat aplikasi Django yang akan memfasilitasi pembuatan migrasi data ini.Menggunakan Django-migrasi-perlengkapan Anda hanya dapat menjalankan perintah manajemen berikut dan akan mencari melalui semua Anda
INSTALLED_APPS
untukinitial_data.*
file dan mengubahnya menjadi migrasi data.Lihat django-migrasi-fixture untuk petunjuk pemasangan / penggunaan.
sumber
Untuk memberikan database Anda beberapa data awal, tulis migrasi data. Dalam migrasi data, gunakan fungsi RunPython untuk memuat data Anda.
Jangan menulis perintah loaddata apa pun karena cara ini sudah usang.
Migrasi data Anda hanya akan dijalankan sekali. Migrasi adalah urutan migrasi yang teratur. Ketika 003_xxxx.py migrations dijalankan, django migrations menulis dalam database bahwa aplikasi ini dimigrasi hingga yang ini (003), dan hanya akan menjalankan migrasi berikut.
sumber
myModel.create(...)
(atau menggunakan loop) di fungsi RunPython?Sayangnya, solusi yang disajikan di atas tidak berhasil untuk saya. Saya menemukan bahwa setiap kali saya mengganti model saya, saya harus memperbarui perlengkapan saya. Idealnya saya akan menulis migrasi data untuk mengubah data yang dibuat dan data yang dimuat fixture dengan cara yang sama.
Untuk memfasilitasi ini saya menulis fungsi cepat yang akan mencari di
fixtures
direktori aplikasi saat ini dan memuat fixture. Letakkan fungsi ini ke dalam migrasi di titik riwayat model yang cocok dengan bidang dalam migrasi.sumber
RunPython(load_fixture('badger', 'stoat'))
. gist.github.com/danni/1b2a0078e998ac080111Menurut saya perlengkapannya agak buruk. Jika database Anda sering berubah, memperbaruinya akan segera menjadi mimpi buruk. Sebenarnya bukan hanya pendapat saya saja, di buku "Two Scoops of Django" dijelaskan jauh lebih baik.
Sebagai gantinya saya akan menulis file Python untuk menyediakan pengaturan awal. Jika Anda membutuhkan sesuatu yang lebih saya sarankan Anda melihat Factory boy .
Jika Anda perlu memigrasi beberapa data, Anda harus menggunakan migrasi data .
Ada juga "Bakar Perlengkapan Anda, Gunakan Pabrik Model" tentang penggunaan perlengkapan.
sumber
Pada Django 2.1, saya ingin memuat beberapa model (Seperti nama negara misalnya) dengan data awal.
Tetapi saya ingin ini terjadi secara otomatis tepat setelah eksekusi migrasi awal.
Jadi saya pikir akan sangat bagus untuk memiliki
sql/
folder di dalam setiap aplikasi yang membutuhkan data awal untuk dimuat.Kemudian di dalam
sql/
folder itu saya akan memiliki.sql
file dengan DML yang diperlukan untuk memuat data awal ke dalam model yang sesuai, misalnya:Untuk lebih deskriptif, seperti inilah tampilan aplikasi yang berisi
sql/
folder:Saya juga menemukan beberapa kasus di mana saya memerlukan
sql
skrip untuk dieksekusi dalam urutan tertentu. Jadi saya memutuskan untuk mengawali nama file dengan nomor yang berurutan seperti yang terlihat pada gambar di atas.Lalu saya membutuhkan cara untuk memuat apa pun
SQLs
tersedia di dalam folder aplikasi apa pun secara otomatis dengan melakukanpython manage.py migrate
.Jadi saya membuat aplikasi lain bernama
initial_data_migrations
dan kemudian saya menambahkan aplikasi ini ke daftarINSTALLED_APPS
dalamsettings.py
file. Kemudian saya membuatmigrations
folder di dalam dan menambahkan file bernamarun_sql_scripts.py
( Yang sebenarnya adalah migrasi khusus ). Seperti yang terlihat pada gambar di bawah ini:Saya membuat
run_sql_scripts.py
agar menangani menjalankan semuasql
skrip yang tersedia dalam setiap aplikasi. Yang ini kemudian ditembakkan saat seseorang laripython manage.py migrate
. Kustom inimigration
juga menambahkan aplikasi yang terlibat sebagai dependensi, dengan cara itu ia mencoba menjalankansql
pernyataan hanya setelah aplikasi yang diperlukan telah mengeksekusi0001_initial.py
migrasinya (Kami tidak ingin mencoba menjalankan pernyataan SQL terhadap tabel yang tidak ada).Inilah sumber dari skrip itu:
Saya harap seseorang menemukan ini bermanfaat, ini bekerja dengan baik untuk saya !. Jika Anda memiliki pertanyaan, beri tahu saya.
CATATAN: Ini mungkin bukan solusi terbaik karena saya baru saja memulai dengan django, namun masih ingin berbagi "How-to" ini dengan Anda semua karena saya tidak menemukan banyak informasi saat googling tentang ini.
sumber