Jadi sekitar setahun yang lalu saya memulai sebuah proyek dan seperti semua pengembang baru saya tidak terlalu fokus pada struktur, tetapi sekarang saya lebih jauh bersama dengan Django sudah mulai tampak bahwa tata letak proyek saya terutama model saya strukturnya mengerikan dalam struktur. .
Saya memiliki sebagian besar model diadakan di satu aplikasi dan benar-benar sebagian besar model ini harus di aplikasi masing-masing, saya memang mencoba dan menyelesaikan ini dan memindahkannya dengan selatan tetapi saya merasa rumit dan sangat sulit karena kunci asing dll.
Namun karena Django 1.7 dan dukungan migrasi yang dibangun, apakah ada cara yang lebih baik untuk melakukannya sekarang?
Jawaban:
Saya menghapus jawaban lama karena dapat menyebabkan hilangnya data. Seperti yang disebutkan ozan , kami dapat membuat 2 migrasi satu di setiap aplikasi. Komentar di bawah posting ini merujuk pada jawaban lama saya.
Migrasi pertama untuk menghapus model dari aplikasi pertama.
Edit file migrasi untuk memasukkan operasi ini.
Migrasi kedua yang bergantung pada migrasi pertama dan membuat tabel baru di aplikasi kedua. Setelah memindahkan kode model ke aplikasi ke-2
dan edit file migrasi ke sesuatu seperti ini.
sumber
./manage.py migrate
semuanya akan berakhir dalam keadaan baik. Memalsukan migrasi secara manual adalah IMO dengan cara yang salah.Ini bisa dilakukan dengan cukup mudah menggunakan
migrations.SeparateDatabaseAndState
. Pada dasarnya, kami menggunakan operasi basis data untuk mengubah nama tabel bersamaan dengan dua operasi negara untuk menghapus model dari satu riwayat aplikasi dan membuatnya di yang lain.Hapus dari aplikasi lama
Dalam migrasi:
Tambahkan ke aplikasi baru
Pertama, salin model ke model.py aplikasi baru, lalu:
Ini akan menghasilkan migrasi dengan
CreateModel
operasi naif sebagai operasi tunggal. Bungkus itu dalamSeparateDatabaseAndState
operasi sehingga kami tidak mencoba membuat ulang tabel. Juga termasuk migrasi sebelumnya sebagai ketergantungan:sumber
Saya mengalami masalah yang sama. Jawaban Ozan membantu saya tetapi sayangnya tidak cukup. Memang saya memiliki beberapa ForeignKey yang menghubungkan ke model yang ingin saya pindahkan. Setelah beberapa sakit kepala saya menemukan solusi sehingga memutuskan untuk mempostingnya untuk menyelesaikan waktu orang.
Anda membutuhkan 2 langkah lagi:
ForeignKey
tautan AndaTheModel
menjadiIntegerfield
. Lalu laripython manage.py makemigrations
ForeignKey(TheModel)
bukanIntegerField()
. Kemudian lakukan migrasi lagi (python manage.py makemigrations
). Anda kemudian dapat bermigrasi dan seharusnya berfungsi (python manage.py migrate
)Semoga ini bisa membantu. Tentu saja mengujinya di lokal sebelum mencoba produksi untuk menghindari kejutan buruk :)
sumber
Bagaimana saya melakukannya (diuji pada Django == 1.8, dengan postgres, jadi mungkin juga 1.7)
Situasi
app1.YourModel
tetapi Anda ingin membuka: app2.YourModel
tambahkan ini ke app2.YourModel:
$ python manage.py makemigrations app2
Migrasi baru (mis. 0009_auto_something.py) dibuat di app2 dengan pernyataan migrasi.CreateModel (), pindahkan pernyataan ini ke migrasi awal app2 (mis. 0001_initial.py) (akan seperti itu selalu ada di sana). Dan sekarang hapus migrasi yang dibuat = 0009_auto_something.py
Sama seperti Anda bertindak, seperti app2.YourModel selalu ada di sana, sekarang hapus keberadaan app1.YourModel dari migrasi Anda. Artinya: beri komentar pada pernyataan CreateModel, dan setiap penyesuaian atau migrasi data yang Anda gunakan setelah itu.
Dan tentu saja, setiap referensi ke app1.YourModel harus diubah ke app2.YourModel melalui proyek Anda. Juga, jangan lupa bahwa semua kunci asing yang mungkin untuk app1.YourModel Anda dalam migrasi harus diubah ke app2.YourModel Anda
Sekarang jika Anda melakukan migrasi $ python manage.py, tidak ada yang berubah, juga ketika Anda melakukan $ emsegemigrasie python manage.py, tidak ada yang baru terdeteksi.
Sekarang sentuhan akhir: hapus Class Meta dari app2.YourModel dan lakukan $ python manage.py makemigrations app2 && python manage.py migrasi app2 (jika Anda melihat migrasi ini Anda akan melihat sesuatu seperti ini :)
table = None, berarti akan mengambil nama tabel default, yang dalam hal ini adalah app2_yourmodel.
PS selama migrasi akan melihat bahwa content_type app1.yourmodel Anda telah dihapus dan dapat dihapus. Anda bisa mengatakan ya untuk itu tetapi hanya jika Anda tidak menggunakannya. Jika Anda sangat bergantung padanya untuk memiliki FK agar jenis kontennya tetap utuh, jangan jawab ya atau belum, tetapi pergilah ke db saat itu secara manual, dan hapus app typey app2.yourmodel, dan ganti nama app typetype1. model Anda ke app2.yourmodel Anda, dan kemudian lanjutkan dengan menjawab tidak.
sumber
app_label = 'app1'
opsi meta.field1 = models.ForeignKey('app1.myModel').
ketika saya bermigrasi, saya mendapatkan ValueError yang menyatakan itufield1 was declared with a lazy reference to 'app1.myModel' but app 'app1' doesn't provide model 'MyModel'
Saya mendapatkan migrasi kode tangan yang gugup (seperti yang diminta oleh jawaban Ozan ) sehingga yang berikut ini menggabungkan strategi Ozan dan Michael untuk meminimalkan jumlah kode tangan yang diperlukan:
makemigrations
.app1
keapp2
Seperti yang direkomendasikan oleh @Michael, kami mengarahkan model baru ke tabel database lama menggunakan
db_table
opsi Meta pada model "baru":Lari
makemigrations
. Ini akan menghasilkanCreateModel
dalamapp2
danDeleteModel
dalamapp1
. Secara teknis, migrasi ini merujuk ke tabel yang sama persis dan akan menghapus (termasuk semua data) dan membuat ulang tabel.Pada kenyataannya, kita tidak ingin (atau perlu) melakukan apa pun di atas meja. Kami hanya perlu Django untuk percaya bahwa perubahan telah dibuat. Per @ Ozan menjawab,
state_operations
bendera diSeparateDatabaseAndState
melakukan ini. Jadi kita membungkus semuamigrations
entri DI KEDUA MIGRASI FILES denganSeparateDatabaseAndState(state_operations=[...])
. Sebagai contoh,menjadi
Anda juga perlu memastikan
CreateModel
migrasi "virtual" baru tergantung pada migrasi apa pun yang benar - benar membuat atau mengubah tabel asli . Misalnya, jika migrasi baru Anda adalahapp2.migrations.0004_auto_<date>
(untukCreate
) danapp1.migrations.0007_auto_<date>
(untukDelete
), hal paling sederhana untuk dilakukan adalah:app1.migrations.0007_auto_<date>
dan salinapp1
ketergantungannya (mis('app1', '0006...'),
.). Ini adalah migrasi "segera sebelum" diapp1
dan harus mencakup dependensi pada semua logika pembangunan model yang sebenarnya.app2.migrations.0004_auto_<date>
dan tambahkan dependensi yang baru saja Anda salin kedependencies
daftar.Jika Anda memiliki
ForeignKey
hubungan dengan model yang Anda pindahkan, hal di atas mungkin tidak berfungsi. Ini terjadi karena:ForeignKey
perubahanForeignKey
perubahanstate_operations
sehingga kami perlu memastikan mereka terpisah dari operasi tabel.CATATAN: Django 2.2 menambahkan peringatan (
models.E028
) yang merusak metode ini. Anda mungkin dapat mengatasinya denganmanaged=False
tetapi saya belum mengujinya.Rangkaian operasi "minimum" berbeda tergantung pada situasinya, tetapi prosedur berikut harus bekerja untuk sebagian besar / semua
ForeignKey
migrasi:app1
keapp2
, setdb_table
, tetapi JANGAN mengubah referensi FK.makemigrations
dan bungkus semuaapp2
migrasi masukstate_operations
(lihat di atas)app2
CreateTable
keapp1
migrasi terbarumodels.py
(JANGAN hapus) sehingga tidak bersaing dengan kelas yang diimpor.Jalankan
makemigrations
tetapi JANGAN masukkan apa punstate_operations
(perubahan FK seharusnya benar-benar terjadi). Tambahkan dependensi di semuaForeignKey
migrasi (yaituAlterField
) keCreateTable
migrasi diapp2
(Anda akan membutuhkan daftar ini untuk langkah selanjutnya jadi tetap ikuti mereka). Sebagai contoh:CreateModel
egapp2.migrations.0002_auto_<date>
dan salin nama migrasi itu.Temukan semua migrasi yang memiliki ForeignKey ke model itu (mis. Dengan mencari
app2.YourModel
untuk menemukan migrasi seperti:Tambahkan
CreateModel
migrasi sebagai ketergantungan:Hapus model dari
app1
makemigrations
dan bungkusapp1
migrasistate_operations
.ForeignKey
migrasi (mis.AlterField
) Dari langkah sebelumnya (mungkin termasuk migrasi dalamapp1
danapp2
).DeleteTable
sudah tergantung padaAlterField
migrasi jadi saya tidak perlu memberlakukannya secara manual (yaituAlter
sebelumnyaDelete
).Pada titik ini, Django sudah bagus. Model baru menunjuk ke tabel lama dan migrasi Django telah meyakinkan bahwa semuanya telah dipindahkan dengan tepat. Peringatan besar (dari jawaban @ Michael) adalah bahwa yang baru
ContentType
dibuat untuk model baru. Jika Anda menautkan (mis. OlehForeignKey
) ke jenis konten, Anda harus membuat migrasi untuk memperbaruiContentType
tabel.Saya ingin membersihkan diri saya (opsi Meta dan nama tabel) jadi saya menggunakan prosedur berikut (dari @Michael):
db_table
entri Metamakemigrations
lagi untuk menghasilkan penggantian nama basis dataDeleteTable
migrasi. Sepertinya tidak perlu karenaDelete
harus murni logis, tetapi saya mengalami kesalahan (misalnyaapp1_yourmodel
tidak ada) jika saya tidak.sumber
table_name: (models.E028) db_table 'table_name' is used by multiple models: app1.Model, app2.Model.
managed=False
tetapi saya tidak ada tempat untuk memeriksa.Alternatif peretasan lain jika datanya tidak besar atau terlalu rumit, tetapi tetap penting untuk dipelihara, adalah dengan:
sumber
Disalin dari jawaban saya di https://stackoverflow.com/a/47392970/8971048
Jika Anda perlu memindahkan model dan Anda tidak memiliki akses ke aplikasi lagi (atau Anda tidak ingin akses), Anda dapat membuat Operasi baru dan mempertimbangkan untuk membuat model baru hanya jika model yang dimigrasi tidak ada.
Dalam contoh ini saya meneruskan 'MyModel' dari old_app ke myapp.
sumber
Ini diuji secara kasar, jadi jangan lupa untuk membuat cadangan DB Anda !!!
Misalnya, ada dua aplikasi:
src_app
dandst_app
, kami ingin memindahkan modelMoveMe
darisrc_app
kedst_app
.Buat migrasi kosong untuk kedua aplikasi:
Mari kita asumsikan, bahwa migrasi baru adalah
XXX1_src_app_new
danXXX1_dst_app_new
, previuos migrasi teratas adalahXXX0_src_app_old
danXXX0_dst_app_old
.Tambahkan operasi yang
MoveMe
mengubah nama tabel untuk model dan ganti nama app_label di ProjectState menjadiXXX1_dst_app_new
. Jangan lupa menambahkan ketergantungan padaXXX0_src_app_old
migrasi.XXX1_dst_app_new
Migrasi yang dihasilkan adalah:Tambahkan ketergantungan
XXX1_dst_app_new
keXXX1_src_app_new
.XXX1_src_app_new
adalah migrasi no-op yang diperlukan untuk memastikan bahwasrc_app
migrasi di masa depan akan dilaksanakan setelahXXX1_dst_app_new
.Pindah
MoveMe
darisrc_app/models.py
kedst_app/models.py
. Lalu lari:Itu saja!
sumber
Anda dapat mencoba yang berikut (belum teruji):
src_app
kedest_app
dest_app
; pastikan migrasi skema tergantung padasrc_app
migrasi terbaru ( https://docs.djangoproject.com/en/dev/topics/migrations/#migration-files )dest_app
, yang menyalin semua data darisrc_app
src_app
; pastikan migrasi skema tergantung pada migrasi (data) terbarudest_app
- yaitu: migrasi langkah 3Perhatikan bahwa Anda akan menjadi menyalin seluruh tabel, alih-alih memindahkannya , tetapi dengan cara itu kedua aplikasi tidak harus menyentuh tabel milik aplikasi lain, yang saya pikir lebih penting.
sumber
Katakanlah Anda sedang memindahkan model TheModel dari app_a ke app_b.
Solusi alternatif adalah mengubah migrasi yang ada dengan tangan. Idenya adalah bahwa setiap kali Anda melihat operasi mengubah TheModel dalam migrasi app_a, Anda menyalin operasi itu ke akhir migrasi awal app_b. Dan setiap kali Anda melihat referensi 'app_a.Model' dalam migrasi app_a, Anda mengubahnya menjadi 'app_b.Model'.
Saya hanya melakukan ini untuk proyek yang ada, di mana saya ingin mengekstraksi model tertentu ke aplikasi yang dapat digunakan kembali. Prosedur berjalan dengan lancar. Saya kira segalanya akan lebih sulit jika ada referensi dari app_b ke app_a. Juga, saya memiliki Meta.db_table yang ditentukan secara manual untuk model saya yang mungkin membantu.
Khususnya Anda akan berakhir dengan riwayat migrasi yang diubah. Ini tidak masalah, bahkan jika Anda memiliki database dengan migrasi asli yang diterapkan. Jika migrasi asli dan yang ditulis ulang berakhir dengan skema database yang sama, maka penulisan ulang seperti itu harus OK.
sumber
Lakukan ini secara terpisah untuk setiap model yang perlu dipindahkan. Saya tidak akan menyarankan melakukan apa yang jawaban lain katakan dengan mengubah ke bilangan bulat dan kembali ke kunci asing. Ada kemungkinan kunci asing baru akan berbeda dan baris mungkin memiliki ID yang berbeda setelah migrasi dan saya tidak ingin mengambil risiko apa pun. dari ketidakcocokan id ketika beralih kembali ke kunci asing.
sumber