Mengelola migrasi basis data merupakan tantangan besar dalam proyek perangkat lunak apa pun. Untungnya, pada versi 1.7, Django hadir dengan kerangka kerja migrasi bawaan. Kerangka kerja ini sangat kuat dan berguna dalam mengelola perubahan dalam basis data. Tetapi fleksibilitas yang diberikan oleh kerangka kerja membutuhkan beberapa kompromi. Untuk memahami batasan dari migrasi Django, Anda akan menangani masalah yang terkenal:membuat indeks di Django tanpa waktu henti.
Dalam tutorial ini, Anda akan mempelajari:
- Bagaimana dan kapan Django menghasilkan migrasi baru
- Cara memeriksa perintah yang dihasilkan Django untuk menjalankan migrasi
- Cara memodifikasi migrasi dengan aman agar sesuai dengan kebutuhan Anda
Tutorial tingkat menengah ini dirancang untuk pembaca yang sudah terbiasa dengan migrasi Django. Untuk pengenalan topik itu, lihat Django Migrations:A Primer.
Bonus Gratis: Klik di sini untuk mendapatkan akses gratis ke tutorial dan sumber daya Django tambahan yang dapat Anda gunakan untuk memperdalam keterampilan pengembangan web Python Anda.
Masalah Dengan Membuat Indeks dalam Migrasi Django
Perubahan umum yang biasanya diperlukan saat data yang disimpan oleh aplikasi Anda bertambah adalah menambahkan indeks. Indeks digunakan untuk mempercepat kueri dan membuat aplikasi Anda terasa cepat dan responsif.
Di sebagian besar database, menambahkan indeks memerlukan kunci eksklusif di atas meja. Kunci eksklusif mencegah operasi modifikasi data (DML) seperti UPDATE
, INSERT
, dan DELETE
, saat indeks dibuat.
Kunci diperoleh secara implisit oleh database saat menjalankan operasi tertentu. Misalnya, ketika pengguna masuk ke aplikasi Anda, Django akan memperbarui last_login
bidang di auth_user
meja. Untuk melakukan pembaruan, database terlebih dahulu harus mendapatkan kunci pada baris. Jika baris saat ini dikunci oleh koneksi lain, Anda mungkin mendapatkan pengecualian database.
Mengunci tabel dapat menimbulkan masalah jika diperlukan untuk menjaga sistem tetap tersedia selama migrasi. Semakin besar tabel, semakin lama waktu yang dibutuhkan untuk membuat indeks. Semakin lama waktu yang dibutuhkan untuk membuat indeks, semakin lama sistem tidak tersedia atau tidak responsif terhadap pengguna.
Beberapa vendor database menyediakan cara untuk membuat indeks tanpa mengunci tabel. Misalnya, untuk membuat indeks di PostgreSQL tanpa mengunci tabel, Anda dapat menggunakan CONCURRENTLY
kata kunci:
CREATE INDEX CONCURRENTLY ix ON table (column);
Di Oracle, ada ONLINE
opsi untuk mengizinkan operasi DML di atas tabel saat indeks dibuat:
CREATE INDEX ix ON table (column) ONLINE;
Saat menghasilkan migrasi, Django tidak akan menggunakan kata kunci khusus ini. Menjalankan migrasi apa adanya akan membuat database memperoleh kunci eksklusif pada tabel dan mencegah operasi DML saat indeks dibuat.
Membuat indeks secara bersamaan memiliki beberapa peringatan. Penting untuk memahami masalah khusus untuk backend database Anda terlebih dahulu. Misalnya, satu peringatan di PostgreSQL adalah membuat indeks secara bersamaan membutuhkan waktu lebih lama karena memerlukan pemindaian tabel tambahan.
Dalam tutorial ini, Anda akan menggunakan migrasi Django untuk membuat indeks pada tabel besar, tanpa menyebabkan waktu henti.
Catatan: Untuk mengikuti tutorial ini, disarankan agar Anda menggunakan backend PostgreSQL, Django 2.x, dan Python 3.
Dimungkinkan untuk mengikuti bersama dengan backend basis data lainnya juga. Di tempat di mana fitur SQL unik untuk PostgreSQL digunakan, ubah SQL agar sesuai dengan backend database Anda.
Penyiapan
Anda akan menggunakan Sale
yang dibuat-buat model dalam aplikasi bernama app
. Dalam situasi kehidupan nyata, model seperti Sale
adalah tabel utama dalam database, dan biasanya berukuran sangat besar dan menyimpan banyak data:
# models.py
from django.db import models
class Sale(models.Model):
sold_at = models.DateTimeField(
auto_now_add=True,
)
charged_amount = models.PositiveIntegerField()
Untuk membuat tabel, buat migrasi awal dan terapkan:
$ python manage.py makemigrations
Migrations for 'app':
app/migrations/0001_initial.py
- Create model Sale
$ python manage migrate
Operations to perform:
Apply all migrations: app
Running migrations:
Applying app.0001_initial... OK
Setelah beberapa saat, tabel penjualan menjadi sangat besar, dan pengguna mulai mengeluh tentang kelambatan. Saat memantau database, Anda melihat bahwa banyak kueri menggunakan sold_at
kolom. Untuk mempercepat, Anda memutuskan bahwa Anda memerlukan indeks pada kolom.
Untuk menambahkan indeks pada sold_at
, Anda membuat perubahan berikut pada model:
# models.py
from django.db import models
class Sale(models.Model):
sold_at = models.DateTimeField(
auto_now_add=True,
db_index=True,
)
charged_amount = models.PositiveIntegerField()
Jika Anda menjalankan migrasi ini sebagaimana adanya, maka Django akan membuat indeks pada tabel, dan itu akan dikunci hingga indeks selesai. Perlu beberapa saat untuk membuat indeks pada tabel yang sangat besar, dan Anda ingin menghindari waktu henti.
Pada lingkungan pengembangan lokal dengan kumpulan data kecil dan koneksi yang sangat sedikit, migrasi ini mungkin terasa instan. Namun, pada kumpulan data besar dengan banyak koneksi bersamaan, memperoleh kunci dan membuat indeks dapat memakan waktu cukup lama.
Pada langkah selanjutnya, Anda akan memodifikasi migrasi yang dibuat oleh Django untuk membuat indeks tanpa menyebabkan waktu henti apa pun.
Migrasi Palsu
Pendekatan pertama adalah membuat indeks secara manual. Anda akan menghasilkan migrasi, tetapi Anda tidak akan benar-benar membiarkan Django menerapkannya. Sebagai gantinya, Anda akan menjalankan SQL secara manual dalam database dan kemudian membuat Django berpikir bahwa migrasi telah selesai.
Pertama, buat migrasi:
$ python manage.py makemigrations --name add_index_fake
Migrations for 'app':
app/migrations/0002_add_index_fake.py
- Alter field sold_at on sale
Gunakan sqlmigrate
perintah untuk melihat SQL Django yang akan digunakan untuk menjalankan migrasi ini:
$ python manage.py sqlmigrate app 0002
BEGIN;
--
-- Alter field sold_at on sale
--
CREATE INDEX "app_sale_sold_at_b9438ae4" ON "app_sale" ("sold_at");
COMMIT;
Anda ingin membuat indeks tanpa mengunci tabel, jadi Anda perlu mengubah perintah. Tambahkan CONCURRENTLY
kata kunci dan jalankan di database:
app=# CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
ON "app_sale" ("sold_at");
CREATE INDEX
Perhatikan bahwa Anda menjalankan perintah tanpa BEGIN
dan COMMIT
bagian. Menghilangkan kata kunci ini akan menjalankan perintah tanpa transaksi database. Kami akan membahas transaksi database nanti di artikel.
Setelah Anda menjalankan perintah, jika Anda mencoba menerapkan migrasi, maka Anda akan mendapatkan kesalahan berikut:
$ python manage.py migrate
Operations to perform:
Apply all migrations: app
Running migrations:
Applying app.0002_add_index_fake...Traceback (most recent call last):
File "venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 85, in _execute
return self.cursor.execute(sql, params)
psycopg2.ProgrammingError: relation "app_sale_sold_at_b9438ae4" already exists
Django mengeluh bahwa indeks sudah ada, sehingga tidak dapat melanjutkan migrasi. Anda baru saja membuat indeks secara langsung dalam database, jadi sekarang Anda perlu membuat Django berpikir bahwa migrasi telah diterapkan.
Cara Memalsukan Migrasi
Django menyediakan cara bawaan untuk menandai migrasi sebagai dieksekusi, tanpa benar-benar mengeksekusinya. Untuk menggunakan opsi ini, setel --fake
tandai saat menerapkan migrasi:
$ python manage.py migrate --fake
Operations to perform:
Apply all migrations: app
Running migrations:
Applying app.0002_add_index_fake... FAKED
Django tidak memunculkan kesalahan kali ini. Faktanya, Django tidak benar-benar menerapkan migrasi apapun. Itu baru saja menandainya sebagai dieksekusi (atau FAKED
).
Berikut beberapa masalah yang perlu dipertimbangkan saat memalsukan migrasi:
-
Perintah manual harus setara dengan SQL yang dihasilkan oleh Django: Anda perlu memastikan perintah yang Anda jalankan setara dengan SQL yang dihasilkan oleh Django. Gunakan
sqlmigrate
untuk menghasilkan perintah SQL. Jika perintah tidak cocok, Anda mungkin akan mengalami inkonsistensi antara database dan status model. -
Migrasi lain yang belum diterapkan juga akan dipalsukan: Jika Anda memiliki beberapa migrasi yang belum diterapkan, semuanya akan dipalsukan. Sebelum Anda menerapkan migrasi, penting untuk memastikan bahwa hanya migrasi yang ingin Anda palsukan yang tidak diterapkan. Jika tidak, Anda mungkin berakhir dengan inkonsistensi. Opsi lainnya adalah menentukan migrasi persis yang ingin Anda palsukan.
-
Diperlukan akses langsung ke database: Anda perlu menjalankan perintah SQL di database. Ini tidak selalu merupakan pilihan. Selain itu, menjalankan perintah secara langsung dalam database produksi berbahaya dan harus dihindari jika memungkinkan.
-
Proses penerapan otomatis mungkin memerlukan penyesuaian: Jika Anda mengotomatiskan proses penerapan (menggunakan CI, CD, atau alat otomatisasi lainnya), Anda mungkin perlu mengubah proses menjadi migrasi palsu. Ini tidak selalu diinginkan.
Pembersihan
Sebelum melanjutkan ke bagian berikutnya, Anda perlu mengembalikan database ke statusnya tepat setelah migrasi awal. Untuk melakukannya, migrasi kembali ke migrasi awal:
$ python manage.py migrate 0001
Operations to perform:
Target specific migration: 0001_initial, from app
Running migrations:
Rendering model states... DONE
Unapplying app.0002_add_index_fake... OK
Django membatalkan penerapan perubahan yang dibuat dalam migrasi kedua, jadi sekarang aman juga untuk menghapus file:
$ rm app/migrations/0002_add_index_fake.py
Untuk memastikan Anda melakukan semuanya dengan benar, periksa migrasi:
$ python manage.py showmigrations app
app
[X] 0001_initial
Migrasi awal telah diterapkan, dan tidak ada migrasi yang belum diterapkan.
Jalankan SQL Mentah dalam Migrasi
Di bagian sebelumnya, Anda mengeksekusi SQL langsung di database dan memalsukan migrasi. Ini menyelesaikan pekerjaan, tetapi ada solusi yang lebih baik.
Django menyediakan cara untuk mengeksekusi SQL mentah dalam migrasi menggunakan RunSQL
. Mari kita coba menggunakannya daripada menjalankan perintah secara langsung di database.
Pertama, buat migrasi kosong baru:
$ python manage.py makemigrations app --empty --name add_index_runsql
Migrations for 'app':
app/migrations/0002_add_index_runsql.py
Selanjutnya, edit file migrasi dan tambahkan RunSQL
operasi:
# migrations/0002_add_index_runsql.py
from django.db import migrations, models
class Migration(migrations.Migration):
atomic = False
dependencies = [
('app', '0001_initial'),
]
operations = [
migrations.RunSQL(
'CREATE INDEX "app_sale_sold_at_b9438ae4" '
'ON "app_sale" ("sold_at");',
),
]
Saat Anda menjalankan migrasi, Anda akan mendapatkan output berikut:
$ python manage.py migrate
Operations to perform:
Apply all migrations: app
Running migrations:
Applying app.0002_add_index_runsql... OK
Ini terlihat bagus, tapi ada masalah. Mari kita coba membuat migrasi lagi:
$ python manage.py makemigrations --name leftover_migration
Migrations for 'app':
app/migrations/0003_leftover_migration.py
- Alter field sold_at on sale
Django menghasilkan migrasi yang sama lagi. Mengapa melakukan itu?
Pembersihan
Sebelum kami dapat menjawab pertanyaan itu, Anda perlu membersihkan dan membatalkan perubahan yang Anda buat pada database. Mulailah dengan menghapus migrasi terakhir. Itu tidak diterapkan, jadi aman untuk dihapus:
$ rm app/migrations/0003_leftover_migration.py
Selanjutnya, buat daftar migrasi untuk app
aplikasi:
$ python manage.py showmigrations app
app
[X] 0001_initial
[X] 0002_add_index_runsql
Migrasi ketiga hilang, tetapi yang kedua diterapkan. Anda ingin kembali ke keadaan setelah migrasi awal. Coba migrasi kembali ke migrasi awal seperti yang Anda lakukan di bagian sebelumnya:
$ python manage.py migrate app 0001
Operations to perform:
Target specific migration: 0001_initial, from app
Running migrations:
Rendering model states... DONE
Unapplying app.0002_add_index_runsql...Traceback (most recent call last):
NotImplementedError: You cannot reverse this operation
Django tidak dapat membalikkan migrasi.
Operasi Migrasi Terbalik
Untuk membalikkan migrasi, Django mengeksekusi tindakan yang berlawanan untuk setiap operasi. Dalam hal ini, kebalikan dari menambahkan indeks adalah dengan menjatuhkannya. Seperti yang telah Anda lihat, saat migrasi dapat dibalik, Anda dapat membatalkan penerapannya. Sama seperti Anda dapat menggunakan checkout
di Git, Anda dapat membalikkan migrasi jika Anda menjalankan migrate
ke migrasi sebelumnya.
Banyak operasi migrasi bawaan sudah menentukan tindakan sebaliknya. Misalnya, tindakan sebaliknya untuk menambahkan bidang adalah dengan menjatuhkan kolom yang sesuai. Tindakan sebaliknya untuk membuat model adalah dengan membuang tabel yang sesuai.
Beberapa operasi migrasi tidak dapat dibalik. Misalnya, tidak ada tindakan sebaliknya untuk menghapus bidang atau model, karena setelah migrasi diterapkan, data akan hilang.
Di bagian sebelumnya, Anda menggunakan RunSQL
operasi. Saat mencoba membalikkan migrasi, Anda mengalami kesalahan. Menurut kesalahan, salah satu operasi dalam migrasi tidak dapat dibatalkan. Django tidak dapat membalikkan SQL mentah secara default. Karena Django tidak memiliki pengetahuan tentang apa yang dieksekusi oleh operasi, itu tidak dapat menghasilkan tindakan yang berlawanan secara otomatis.
Cara Membuat Migrasi Dapat Dibalik
Untuk migrasi menjadi reversibel, semua operasi di dalamnya harus reversibel. Bagian dari migrasi tidak dapat dibalik, jadi satu operasi yang tidak dapat dibalikkan akan membuat seluruh migrasi tidak dapat dibatalkan.
Untuk membuat RunSQL
operasi reversibel, Anda harus menyediakan SQL untuk dijalankan ketika operasi dibalik. SQL terbalik disediakan di reverse_sql
argumen.
Tindakan kebalikan dari menambahkan indeks adalah dengan menjatuhkannya. Untuk membuat migrasi Anda dapat dibalik, berikan reverse_sql
untuk menjatuhkan indeks:
# migrations/0002_add_index_runsql.py
from django.db import migrations, models
class Migration(migrations.Migration):
atomic = False
dependencies = [
('app', '0001_initial'),
]
operations = [
migrations.RunSQL(
'CREATE INDEX "app_sale_sold_at_b9438ae4" '
'ON "app_sale" ("sold_at");',
reverse_sql='DROP INDEX "app_sale_sold_at_b9438ae4";',
),
]
Sekarang coba balikkan migrasi:
$ python manage.py showmigrations app
app
[X] 0001_initial
[X] 0002_add_index_runsql
$ python manage.py migrate app 0001
Operations to perform:
Target specific migration: 0001_initial, from app
Running migrations:
Rendering model states... DONE
Unapplying app.0002_add_index_runsql... OK
$ python manage.py showmigrations app
app
[X] 0001_initial
[ ] 0002_add_index_runsql
Migrasi kedua dibalik, dan indeks dijatuhkan oleh Django. Sekarang aman untuk menghapus file migrasi:
$ rm app/migrations/0002_add_index_runsql.py
Itu selalu merupakan ide yang baik untuk memberikan reverse_sql
. Dalam situasi di mana membalikkan operasi SQL mentah tidak memerlukan tindakan apa pun, Anda dapat menandai operasi sebagai reversibel menggunakan penjaga khusus migrations.RunSQL.noop
:
migrations.RunSQL(
sql='...', # Your forward SQL here
reverse_sql=migrations.RunSQL.noop,
),
Memahami Status Model dan Status Basis Data
Dalam upaya Anda sebelumnya untuk membuat indeks secara manual menggunakan RunSQL
, Django menghasilkan migrasi yang sama berulang-ulang meskipun indeks telah dibuat dalam database. Untuk memahami mengapa Django melakukan itu, pertama-tama Anda perlu memahami bagaimana Django memutuskan kapan menghasilkan migrasi baru.
Saat Django Menghasilkan Migrasi Baru
Dalam proses menghasilkan dan menerapkan migrasi, Django menyinkronkan antara status basis data dan status model. Misalnya, saat Anda menambahkan bidang ke model, Django menambahkan kolom ke tabel. Saat Anda menghapus bidang dari model, Django menghapus kolom dari tabel.
Untuk menyinkronkan antara model dan database, Django mempertahankan status yang mewakili model. Untuk menyinkronkan database dengan model, Django menghasilkan operasi migrasi. Operasi migrasi diterjemahkan ke SQL khusus vendor yang dapat dieksekusi dalam database. Ketika semua operasi migrasi dijalankan, database dan model diharapkan konsisten.
Untuk mendapatkan status database, Django menggabungkan operasi dari semua migrasi sebelumnya. Ketika keadaan migrasi gabungan tidak konsisten dengan keadaan model, Django menghasilkan migrasi baru.
Pada contoh sebelumnya, Anda membuat indeks menggunakan SQL mentah. Django tidak tahu Anda membuat indeks karena Anda tidak menggunakan operasi migrasi yang sudah dikenal.
Ketika Django mengumpulkan semua migrasi dan membandingkannya dengan keadaan model, ditemukan bahwa sebuah indeks hilang. Inilah sebabnya, bahkan setelah Anda membuat indeks secara manual, Django masih berpikir itu hilang dan menghasilkan migrasi baru untuknya.
Cara Memisahkan Basis Data dan Status dalam Migrasi
Karena Django tidak dapat membuat indeks seperti yang Anda inginkan, Anda ingin memberikan SQL Anda sendiri tetapi tetap memberi tahu Django bahwa Anda yang membuatnya.
Dengan kata lain, Anda perlu mengeksekusi sesuatu dalam database dan menyediakan Django dengan operasi migrasi untuk menyinkronkan keadaan internalnya. Untuk melakukan itu, Django memberi kita operasi migrasi khusus yang disebut SeparateDatabaseAndState
. Operasi ini tidak diketahui dengan baik dan harus disediakan untuk kasus khusus seperti ini.
Mengedit migrasi jauh lebih mudah daripada menulisnya dari awal, jadi mulailah dengan membuat migrasi dengan cara biasa:
$ python manage.py makemigrations --name add_index_separate_database_and_state
Migrations for 'app':
app/migrations/0002_add_index_separate_database_and_state.py
- Alter field sold_at on sale
Ini adalah isi dari migrasi yang dihasilkan oleh Django, sama seperti sebelumnya:
# migrations/0002_add_index_separate_database_and_state.py
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='sale',
name='sold_at',
field=models.DateTimeField(
auto_now_add=True,
db_index=True,
),
),
]
Django menghasilkan AlterField
operasi di bidang sold_at
. Operasi akan membuat indeks dan memperbarui status. Kami ingin mempertahankan operasi ini tetapi memberikan perintah yang berbeda untuk dijalankan di database.
Sekali lagi, untuk mendapatkan perintah, gunakan SQL yang dihasilkan oleh Django:
$ python manage.py sqlmigrate app 0002
BEGIN;
--
-- Alter field sold_at on sale
--
CREATE INDEX "app_sale_sold_at_b9438ae4" ON "app_sale" ("sold_at");
COMMIT;
Tambahkan CONCURRENTLY
kata kunci di tempat yang sesuai:
CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
ON "app_sale" ("sold_at");
Selanjutnya, edit file migrasi dan gunakan SeparateDatabaseAndState
untuk memberikan perintah SQL Anda yang dimodifikasi untuk dieksekusi:
# migrations/0002_add_index_separate_database_and_state.py
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0001_initial'),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.AlterField(
model_name='sale',
name='sold_at',
field=models.DateTimeField(
auto_now_add=True,
db_index=True,
),
),
],
database_operations=[
migrations.RunSQL(sql="""
CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
ON "app_sale" ("sold_at");
""", reverse_sql="""
DROP INDEX "app_sale_sold_at_b9438ae4";
"""),
],
),
],
Operasi migrasi SeparateDatabaseAndState
menerima 2 daftar operasi:
- operasi_status adalah operasi untuk diterapkan pada keadaan model internal. Mereka tidak mempengaruhi database.
- operasi_database adalah operasi untuk diterapkan ke database.
Anda menyimpan operasi asli yang dihasilkan oleh Django di state_operations
. Saat menggunakan SeparateDatabaseAndState
, inilah yang biasanya ingin Anda lakukan. Perhatikan bahwa db_index=True
argumen disediakan ke lapangan. Operasi migrasi ini akan memberi tahu Django bahwa ada indeks pada bidang.
Anda menggunakan SQL yang dihasilkan oleh Django dan menambahkan CONCURRENTLY
kata kunci. Anda menggunakan tindakan khusus RunSQL
untuk mengeksekusi SQL mentah dalam migrasi.
Jika Anda mencoba menjalankan migrasi, Anda akan mendapatkan output berikut:
$ python manage.py migrate app
Operations to perform:
Apply all migrations: app
Running migrations:
Applying app.0002_add_index_separate_database_and_state...Traceback (most recent call last):
File "/venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 83, in _execute
return self.cursor.execute(sql)
psycopg2.InternalError: CREATE INDEX CONCURRENTLY cannot run inside a transaction block
Migrasi Non-Atom
Dalam SQL, CREATE
, DROP
, ALTER
, dan TRUNCATE
operasi disebut sebagai Bahasa Definisi Data (DDL). Dalam basis data yang mendukung DDL transaksional, seperti PostgreSQL, Django mengeksekusi migrasi di dalam transaksi basis data secara default. Namun, menurut kesalahan di atas, PostgreSQL tidak dapat membuat indeks secara bersamaan di dalam blok transaksi.
Untuk dapat membuat indeks secara bersamaan di dalam migrasi, Anda perlu memberi tahu Django untuk tidak menjalankan migrasi dalam transaksi basis data. Untuk melakukannya, Anda menandai migrasi sebagai non-atom dengan menyetel atomic
menjadi False
:
# migrations/0002_add_index_separate_database_and_state.py
from django.db import migrations, models
class Migration(migrations.Migration):
atomic = False
dependencies = [
('app', '0001_initial'),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.AlterField(
model_name='sale',
name='sold_at',
field=models.DateTimeField(
auto_now_add=True,
db_index=True,
),
),
],
database_operations=[
migrations.RunSQL(sql="""
CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
ON "app_sale" ("sold_at");
""",
reverse_sql="""
DROP INDEX "app_sale_sold_at_b9438ae4";
"""),
],
),
],
Setelah Anda menandai migrasi sebagai non-atom, Anda dapat menjalankan migrasi:
$ python manage.py migrate app
Operations to perform:
Apply all migrations: app
Running migrations:
Applying app.0002_add_index_separate_database_and_state... OK
Anda baru saja menjalankan migrasi tanpa menyebabkan waktu henti.
Berikut adalah beberapa masalah yang perlu dipertimbangkan saat Anda menggunakan SeparateDatabaseAndState
:
-
Operasi basis data harus setara dengan operasi status: Inkonsistensi antara database dan status model dapat menyebabkan banyak masalah. Titik awal yang baik adalah menjaga operasi yang dihasilkan oleh Django dalam
state_operations
dan edit outputsqlmigrate
untuk digunakan dalamdatabase_operations
. -
Migrasi non-atomik tidak dapat dikembalikan jika terjadi kesalahan: Jika ada kesalahan selama migrasi, maka Anda tidak akan dapat melakukan rollback. Anda harus mengembalikan migrasi atau menyelesaikannya secara manual. Ada baiknya untuk menjaga agar operasi tetap dijalankan di dalam migrasi non-atomik seminimal mungkin. Jika Anda memiliki operasi tambahan dalam migrasi, pindahkan ke migrasi baru.
-
Migrasi mungkin khusus vendor: SQL yang dihasilkan oleh Django khusus untuk basis data backend yang digunakan dalam proyek. Ini mungkin bekerja dengan backend database lain, tetapi itu tidak dijamin. Jika Anda perlu mendukung beberapa backend database, Anda perlu melakukan beberapa penyesuaian pada pendekatan ini.
Kesimpulan
Anda memulai tutorial ini dengan sebuah tabel besar dan sebuah masalah. Anda ingin membuat aplikasi Anda lebih cepat bagi pengguna, dan Anda ingin melakukannya tanpa menyebabkan mereka downtime.
Di akhir tutorial, Anda berhasil membuat dan memodifikasi migrasi Django dengan aman untuk mencapai tujuan ini. Anda telah mengatasi berbagai masalah selama prosesnya dan berhasil mengatasinya menggunakan alat bawaan yang disediakan oleh kerangka kerja migrasi.
Dalam tutorial ini, Anda mempelajari hal berikut:
- Bagaimana migrasi Django bekerja secara internal menggunakan model dan status basis data, dan ketika migrasi baru dihasilkan
- Cara menjalankan SQL khusus dalam migrasi menggunakan
RunSQL
tindakan - Apa itu migrasi reversibel, dan cara membuat
RunSQL
tindakan dapat dibalik - Apa itu migrasi atom, dan bagaimana mengubah perilaku default sesuai kebutuhan Anda
- Cara menjalankan migrasi kompleks dengan aman di Django
Pemisahan antara model dan status database merupakan konsep penting. Setelah Anda memahaminya, dan cara menggunakannya, Anda dapat mengatasi banyak keterbatasan operasi migrasi bawaan. Beberapa kasus penggunaan yang muncul dalam pikiran termasuk menambahkan indeks yang sudah dibuat dalam database dan memberikan argumen khusus vendor ke perintah DDL.