Ini adalah artikel kedua dalam seri migrasi Django kami:
- Bagian 1:Migrasi Django:Dasar
- Bagian 2:Menggali Lebih Dalam Migrasi Django (artikel saat ini)
- Bagian 3:Migrasi Data
- Video:Django 1.7 Migrasi - Dasar
Dalam artikel sebelumnya dalam seri ini, Anda telah mempelajari tentang tujuan migrasi Django. Anda telah terbiasa dengan pola penggunaan mendasar seperti membuat dan menerapkan migrasi. Sekarang saatnya untuk menggali lebih dalam ke dalam sistem migrasi dan mengintip beberapa mekanisme yang mendasarinya.
Di akhir artikel ini, Anda akan mengetahui:
- Bagaimana Django melacak migrasi
- Bagaimana migrasi mengetahui operasi database mana yang harus dilakukan
- Bagaimana dependensi antarmigrasi didefinisikan
Setelah Anda membungkus kepala Anda di sekitar bagian dari sistem migrasi Django ini, Anda akan dipersiapkan dengan baik untuk membuat migrasi kustom Anda sendiri. Mari kita lompat ke bagian yang terakhir kita tinggalkan!
Artikel ini menggunakan bitcoin_tracker
Proyek Django dibangun di Django Migrations:A Primer. Anda dapat membuat ulang proyek itu dengan membaca artikel itu atau mengunduh kode sumbernya:
Unduh Kode Sumber: Klik di sini untuk mengunduh kode untuk proyek migrasi Django yang akan Anda gunakan dalam artikel ini.
Bagaimana Django Mengetahui Migrasi Yang Harus Diterapkan
Mari kita rekap langkah terakhir dari artikel sebelumnya dalam seri ini. Anda membuat migrasi, lalu menerapkan semua migrasi yang tersedia dengan python manage.py migrate
.Jika perintah itu berhasil dijalankan, maka tabel database Anda sekarang cocok dengan definisi model Anda.
Apa yang terjadi jika Anda menjalankan perintah itu lagi? Mari kita coba:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
No migrations to apply.
Tidak terjadi apa-apa! Setelah migrasi telah diterapkan ke database, Django tidak akan menerapkan migrasi ini ke database tertentu itu lagi. Memastikan bahwa migrasi diterapkan hanya sekali memerlukan pelacakan migrasi yang telah diterapkan.
Django menggunakan tabel database yang disebut django_migrations
. Django secara otomatis membuat tabel ini dalam database Anda saat pertama kali Anda menerapkan migrasi apa pun. Untuk setiap migrasi yang diterapkan atau dipalsukan, baris baru dimasukkan ke dalam tabel.
Misalnya, inilah tampilan tabel di bitcoin_tracker
kami proyek:
ID | Aplikasi | Nama | Diterapkan |
---|---|---|---|
1 | contenttypes | 0001_initial | 05-02-2019 20:23:21.461496 |
2 | auth | 0001_initial | 05-02-2019 20:23:21.489948 |
3 | admin | 0001_initial | 05-02-2019 20:23:21.508742 |
4 | admin | 0002_logentry_remove... | 05-02-2019 20:23:21.531390 |
5 | admin | 0003_logentry_add_ac... | 05-02-2019 20:23:21.564834 |
6 | contenttypes | 0002_remove_content_... | 05-02-2019 20:23:21.597186 |
7 | auth | 0002_alter_permissio... | 05-02-2019 20:23:21.608705 |
8 | auth | 0003_alter_user_emai... | 05-02-2019 20:23:21.628441 |
9 | auth | 0004_alter_user_user... | 05-02-2019 20:23:21.646824 |
10 | auth | 0005_alter_user_last... | 05-02-2019 20:23:21.661182 |
11 | auth | 0006_require_content... | 05-02-2019 20:23:21.663664 |
12 | auth | 0007_alter_validator... | 05-02-2019 20:23:21.679482 |
13 | auth | 0008_alter_user_user... | 05-02-2019 20:23:21.699201 |
14 | auth | 0009_alter_user_last... | 05-02-2019 20:23:21.718652 |
15 | historical_data | 0001_initial | 05-02-2019 20:23:21.726000 |
16 | sessions | 0001_initial | 05-02-2019 20:23:21.734611 |
19 | historical_data | 0002_switch_to_decimals | 05-02-2019 20:30:11.337894 |
Seperti yang Anda lihat, ada entri untuk setiap migrasi yang diterapkan. Tabel tidak hanya berisi migrasi dari historical_data
aplikasi, tetapi juga migrasi dari semua aplikasi terpasang lainnya.
Saat berikutnya migrasi dijalankan, Django akan melewati migrasi yang terdaftar di tabel database. Ini berarti, bahkan jika Anda secara manual mengubah file migrasi yang telah diterapkan, Django akan mengabaikan perubahan ini, selama sudah ada entri untuk itu dalam database.
Anda dapat mengelabui Django untuk menjalankan kembali migrasi dengan menghapus baris yang sesuai dari tabel, tetapi ini jarang merupakan ide yang baik dan dapat meninggalkan Anda dengan sistem migrasi yang rusak.
File Migrasi
Apa yang terjadi ketika Anda menjalankan python manage.py makemigrations <appname>
? Django mencari perubahan yang dibuat pada model di aplikasi Anda <appname>
. Jika ditemukan, seperti model yang telah ditambahkan, maka file migrasi akan dibuat di migrations
subdirektori. File migrasi ini berisi daftar operasi untuk menyelaraskan skema database Anda dengan definisi model Anda.
Catatan: Aplikasi Anda harus terdaftar di INSTALLED_APPS
pengaturan, dan itu harus berisi migrations
direktori dengan __init__.py
mengajukan. Jika tidak, Django tidak akan membuat migrasi apapun untuknya.
migrations
direktori dibuat secara otomatis saat Anda membuat aplikasi baru dengan startapp
perintah manajemen, tetapi mudah dilupakan saat membuat aplikasi secara manual.
File migrasi hanya Python, jadi mari kita lihat file migrasi pertama di historical_prices
aplikasi. Anda dapat menemukannya di historical_prices/migrations/0001_initial.py
. Seharusnya terlihat seperti ini:
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = []
operations = [
migrations.CreateModel(
name='PriceHistory',
fields=[
('id', models.AutoField(
verbose_name='ID',
serialize=False,
primary_key=True,
auto_created=True)),
('date', models.DateTimeField(auto_now_add=True)),
('price', models.DecimalField(decimal_places=2, max_digits=5)),
('volume', models.PositiveIntegerField()),
('total_btc', models.PositiveIntegerField()),
],
options={
},
bases=(models.Model,),
),
]
Seperti yang Anda lihat, ini berisi satu kelas yang disebut Migration
yang mewarisi dari django.db.migrations.Migration
. Ini adalah kelas yang akan dicari dan dijalankan oleh kerangka kerja migrasi saat Anda memintanya untuk menerapkan migrasi.
Migration
kelas berisi dua daftar utama:
dependencies
operations
Operasi Migrasi
Mari kita lihat operations
daftar dulu. Tabel ini berisi operasi yang akan dilakukan sebagai bagian dari migrasi. Operasi adalah subkelas dari kelas django.db.migrations.operations.base.Operation
. Berikut adalah operasi umum yang dibangun ke dalam Django:
Kelas Operasi | Deskripsi |
---|---|
CreateModel | Membuat model baru dan tabel database yang sesuai |
DeleteModel | Menghapus model dan menghapus tabel databasenya |
RenameModel | Mengganti nama model dan mengganti nama tabel databasenya |
AlterModelTable | Mengganti nama tabel database untuk model |
AlterUniqueTogether | Mengubah batasan unik model |
AlterIndexTogether | Mengubah indeks model |
AlterOrderWithRespectTo | Membuat atau menghapus _order kolom untuk model |
AlterModelOptions | Mengubah berbagai pilihan model tanpa mempengaruhi database |
AlterModelManagers | Mengubah pengelola yang tersedia selama migrasi |
AddField | Menambahkan bidang ke model dan kolom yang sesuai di database |
RemoveField | Menghapus bidang dari model dan menghapus kolom yang sesuai dari database |
AlterField | Mengubah definisi bidang dan mengubah kolom basis datanya jika perlu |
RenameField | Mengganti nama bidang dan, jika perlu, juga kolom basis datanya |
AddIndex | Membuat indeks dalam tabel database untuk model |
RemoveIndex | Menghapus indeks dari tabel database untuk model |
Perhatikan bagaimana operasi diberi nama setelah perubahan yang dibuat pada definisi model, bukan tindakan yang dilakukan pada database. Saat Anda menerapkan migrasi, setiap operasi bertanggung jawab untuk menghasilkan pernyataan SQL yang diperlukan untuk database spesifik Anda. Misalnya, CreateModel
akan menghasilkan CREATE TABLE
Pernyataan SQL.
Di luar kotak, migrasi memiliki dukungan untuk semua basis data standar yang didukung Django. Jadi, jika Anda tetap berpegang pada operasi yang tercantum di sini, maka Anda dapat melakukan lebih banyak atau lebih sedikit perubahan apa pun pada model yang Anda inginkan, tanpa harus khawatir tentang SQL yang mendasarinya. Itu semua dilakukan untuk Anda.
Catatan: Dalam beberapa kasus, Django mungkin tidak mendeteksi perubahan Anda dengan benar. Jika Anda mengganti nama model dan mengubah beberapa bidangnya, maka Django mungkin salah mengira ini sebagai model baru.
Alih-alih RenameModel
dan beberapa AlterField
operasi, itu akan membuat DeleteModel
dan CreateModel
operasi. Alih-alih mengganti nama tabel database untuk model, itu akan menjatuhkannya dan membuat tabel baru dengan nama baru, secara efektif menghapus semua data Anda!
Biasakan untuk memeriksa migrasi yang dihasilkan dan mengujinya pada salinan database Anda sebelum menjalankannya pada data produksi.
Django menyediakan tiga kelas operasi lagi untuk kasus penggunaan tingkat lanjut:
RunSQL
memungkinkan Anda untuk menjalankan SQL kustom dalam database.RunPython
memungkinkan Anda menjalankan kode Python apa pun.SeparateDatabaseAndState
adalah operasi khusus untuk penggunaan tingkat lanjut.
Dengan operasi ini, pada dasarnya Anda dapat melakukan perubahan apa pun yang Anda inginkan pada database Anda. Namun, Anda tidak akan menemukan operasi ini dalam migrasi yang telah dibuat secara otomatis dengan makemigrations
perintah manajemen.
Sejak Django 2.0, ada juga beberapa operasi khusus PostgreSQL yang tersedia di django.contrib.postgres.operations
yang dapat Anda gunakan untuk menginstal berbagai ekstensi PostgreSQL:
BtreeGinExtension
BtreeGistExtension
CITextExtension
CryptoExtension
HStoreExtension
TrigramExtension
UnaccentExtension
Perhatikan bahwa migrasi yang berisi salah satu operasi ini memerlukan pengguna database dengan hak pengguna super.
Last but not least, Anda juga dapat membuat kelas operasi Anda sendiri. Jika Anda ingin melihat ke dalamnya, maka lihatlah dokumentasi Django tentang pembuatan operasi migrasi kustom.
Ketergantungan Migrasi
dependencies
list di kelas migrasi berisi migrasi apa pun yang harus diterapkan sebelum migrasi ini dapat diterapkan.
Dalam 0001_initial.py
migrasi yang Anda lihat di atas, tidak ada yang harus diterapkan sebelumnya sehingga tidak ada ketergantungan. Mari kita lihat migrasi kedua di historical_prices
aplikasi. Dalam file 0002_switch_to_decimals.py
, dependencies
atribut Migration
memiliki entri:
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('historical_data', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='pricehistory',
name='volume',
field=models.DecimalField(decimal_places=3, max_digits=7),
),
]
Ketergantungan di atas mengatakan bahwa migrasi 0001_initial
dari aplikasi historical_data
harus dijalankan terlebih dahulu. Itu masuk akal, karena migrasi 0001_initial
membuat tabel yang berisi bidang yang migrasi 0002_switch_to_decimals
ingin berubah.
Migrasi juga dapat memiliki ketergantungan pada migrasi dari aplikasi lain, seperti ini:
class Migration(migrations.Migration):
...
dependencies = [
('auth', '0009_alter_user_last_name_max_length'),
]
Ini biasanya diperlukan jika model memiliki Kunci Asing yang menunjuk ke model di aplikasi lain.
Atau, Anda juga dapat menerapkan bahwa migrasi dijalankan sebelum migrasi lain menggunakan atribut run_before
:
class Migration(migrations.Migration):
...
run_before = [
('third_party_app', '0001_initial'),
]
Dependensi juga dapat digabungkan sehingga Anda dapat memiliki banyak dependensi. Fungsionalitas ini memberikan banyak fleksibilitas, karena Anda dapat mengakomodasi kunci asing yang bergantung pada model dari aplikasi yang berbeda.
Opsi untuk secara eksplisit mendefinisikan ketergantungan antar migrasi juga berarti bahwa penomoran migrasi (biasanya 0001
, 0002
, 0003
, ...) tidak sepenuhnya mewakili urutan penerapan migrasi. Anda dapat menambahkan ketergantungan apa pun yang Anda inginkan dan dengan demikian mengontrol pesanan tanpa harus memberi nomor ulang semua migrasi.
Melihat Migrasi
Anda biasanya tidak perlu khawatir tentang SQL yang dihasilkan oleh migrasi. Tetapi jika Anda ingin memeriksa ulang apakah SQL yang dihasilkan masuk akal atau hanya ingin tahu seperti apa tampilannya, maka Django telah membantu Anda dengan sqlmigrate
perintah manajemen:
$ python manage.py sqlmigrate historical_data 0001
BEGIN;
--
-- Create model PriceHistory
--
CREATE TABLE "historical_data_pricehistory" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"date" datetime NOT NULL,
"price" decimal NOT NULL,
"volume" integer unsigned NOT NULL
);
COMMIT;
Melakukannya akan mencantumkan kueri SQL yang mendasari yang akan dihasilkan oleh migrasi yang ditentukan, berdasarkan database di settings.py
Anda mengajukan. Saat Anda melewati parameter --backwards
, Django menghasilkan SQL untuk membatalkan penerapan migrasi:
$ python manage.py sqlmigrate --backwards historical_data 0001
BEGIN;
--
-- Create model PriceHistory
--
DROP TABLE "historical_data_pricehistory";
COMMIT;
Setelah Anda melihat output dari sqlmigrate
untuk migrasi yang sedikit lebih rumit, Anda mungkin menyadari bahwa Anda tidak perlu membuat semua SQL ini dengan tangan!
Bagaimana Django Mendeteksi Perubahan pada Model Anda
Anda telah melihat seperti apa file migrasi dan bagaimana daftar Operation
kelas mendefinisikan perubahan yang dilakukan ke database. Tetapi bagaimana tepatnya Django mengetahui operasi mana yang harus masuk ke dalam file migrasi? Anda mungkin berharap bahwa Django membandingkan model Anda dengan skema basis data Anda, tetapi bukan itu masalahnya.
Saat menjalankan makemigrations
, Django tidak memeriksa database Anda. Juga tidak membandingkan file model Anda dengan versi sebelumnya. Sebagai gantinya, Django melewati semua migrasi yang telah diterapkan dan membangun status proyek seperti apa model seharusnya. Status proyek ini kemudian dibandingkan dengan definisi model Anda saat ini, dan daftar operasi dibuat, yang bila diterapkan, akan memperbarui status proyek dengan definisi model.
Bermain Catur Dengan Django
Anda dapat menganggap model Anda seperti papan catur, dan Django adalah grandmaster catur yang melihat Anda bermain melawan diri sendiri. Tetapi grandmaster tidak memperhatikan setiap gerakan Anda. Grandmaster hanya melihat papan saat Anda meneriakkan makemigrations
.
Karena hanya ada satu set gerakan yang mungkin (dan grandmaster adalah seorang grandmaster), dia dapat membuat gerakan yang telah terjadi sejak terakhir kali dia melihat papan. Dia membuat beberapa catatan dan membiarkanmu bermain sampai kamu berteriak makemigrations
lagi.
Saat melihat papan di lain waktu, grandmaster tidak ingat seperti apa papan catur terakhir kali, tapi dia bisa membaca catatannya tentang gerakan sebelumnya dan membangun model mental seperti apa papan catur itu.
Sekarang, ketika Anda berteriak migrations
, grandmaster akan memutar ulang semua gerakan yang direkam di papan catur lain dan mencatat di spreadsheet mana catatannya telah diterapkan. Papan catur kedua ini adalah database Anda, dan spreadsheet adalah django_migrations
tabel.
Analogi ini cukup pas, karena menggambarkan dengan baik beberapa perilaku migrasi Django:
-
Migrasi Django mencoba menjadi efisien: Sama seperti grandmaster berasumsi bahwa Anda membuat jumlah gerakan paling sedikit, Django akan mencoba membuat migrasi paling efisien. Jika Anda menambahkan bidang bernama
A
menjadi model, lalu ganti namanya menjadiB
, lalu jalankanmakemigrations
, maka Django akan membuat migrasi baru untuk menambahkan bidang bernamaB
. -
Migrasi Django memiliki batasnya: Jika Anda melakukan banyak gerakan sebelum membiarkan grandmaster melihat papan catur, maka dia mungkin tidak dapat menelusuri kembali gerakan yang tepat dari setiap bidak. Demikian pula, Django mungkin tidak muncul dengan migrasi yang benar jika Anda membuat terlalu banyak perubahan sekaligus.
-
Migrasi Django mengharapkan Anda untuk bermain sesuai aturan: Ketika Anda melakukan sesuatu yang tidak terduga, seperti mengambil bagian acak dari papan atau mengotak-atik catatan, grandmaster mungkin tidak menyadarinya pada awalnya, tetapi cepat atau lambat, dia akan mengangkat tangannya dan menolak untuk melanjutkan. Hal yang sama terjadi ketika Anda mengacaukan
django_migrations
tabel atau ubah skema database Anda di luar migrasi, misalnya dengan menghapus tabel database untuk suatu model.
Memahami SeparateDatabaseAndState
Sekarang setelah Anda mengetahui tentang status proyek yang Django bangun, saatnya untuk melihat lebih dekat pada operasi SeparateDatabaseAndState
. Operasi ini dapat melakukan persis seperti yang tersirat dari namanya:ia dapat memisahkan status proyek (model mental yang dibangun Django) dari database Anda.
SeparateDatabaseAndState
dipakai dengan dua daftar operasi:
state_operations
berisi operasi yang hanya diterapkan pada status proyek.database_operations
berisi operasi yang hanya diterapkan ke database.
Operasi ini memungkinkan Anda melakukan segala jenis perubahan pada database Anda, tetapi Anda bertanggung jawab untuk memastikan bahwa status proyek sesuai dengan database setelahnya. Contoh kasus penggunaan untuk SeparateDatabaseAndState
sedang memindahkan model dari satu aplikasi ke aplikasi lain atau membuat indeks pada database besar tanpa waktu henti.
SeparateDatabaseAndState
adalah operasi lanjutan dan Anda tidak perlu pada hari pertama bekerja dengan migrasi dan mungkin tidak pernah sama sekali. SeparateDatabaseAndState
mirip dengan operasi jantung. Ini membawa sedikit risiko dan bukan sesuatu yang Anda lakukan hanya untuk bersenang-senang, tetapi terkadang ini adalah prosedur yang diperlukan untuk menjaga pasien tetap hidup.
Kesimpulan
Ini menyimpulkan penyelaman mendalam Anda ke dalam migrasi Django. Selamat! Anda telah membahas cukup banyak topik lanjutan dan sekarang memiliki pemahaman yang kuat tentang apa yang terjadi di balik kap migrasi.
Anda mengetahui bahwa:
- Django melacak migrasi yang diterapkan dalam tabel migrasi Django.
- Migrasi Django terdiri dari file Python biasa yang berisi
Migration
kelas. - Django mengetahui perubahan mana yang harus dilakukan dari
operations
daftar diMigration
kelas. - Django membandingkan model Anda dengan status proyek yang dibangunnya dari migrasi.
Dengan pengetahuan ini, Anda sekarang siap untuk menangani bagian ketiga dari seri tentang migrasi Django, di mana Anda akan mempelajari cara menggunakan migrasi data untuk membuat perubahan satu kali dengan aman pada data Anda. Tetap disini!
Artikel ini menggunakan bitcoin_tracker
Proyek Django dibangun di Django Migrations:A Primer. Anda dapat membuat ulang proyek itu dengan membaca artikel itu atau mengunduh kode sumbernya:
Unduh Kode Sumber: Klik di sini untuk mengunduh kode untuk proyek migrasi Django yang akan Anda gunakan dalam artikel ini.