Migration؛ تاریخچهی نسخهدار پایگاه داده
هر بار مدل را تغییر میدهید، makemigrations یک فایل پایتونی میسازد که فقط «تفاوت» را توصیف میکند و migrate آن را روی پایگاه داده اجرا میکند. این فایلها بخشی از کد پروژهاند و باید در git کامیت شوند؛ سرور هرگز نباید خودش makemigrations بزند.
| دستور | کاربرد |
|---|---|
makemigrations orders | ساخت migration برای یک اپ |
migrate | اجرای همهی migrationهای اجرانشده |
showmigrations orders | فهرست با علامت [X] برای اجراشدهها |
sqlmigrate orders 0005 | SQL دقیقی که اجرا خواهد شد |
migrate orders 0004 | برگشت به یک نقطهی قبلی |
migrate --plan | نمایش ترتیب اجرا بدون اجرا |
makemigrations --merge | حل تداخل دو شاخه |
squashmigrations orders 0001 0040 | ادغام چند migration در یکی |
Data migration با RunPython
فرض کنید فیلد tracking_code را اضافه کردهاید و باید برای هزاران سفارش قدیمی مقدار بسازید. یک migration خالی بسازید و منطق را در آن بنویسید:
python manage.py makemigrations orders --empty --name fill_tracking_codes
from django.db import migrations
def fill_codes(apps, schema_editor):
Order = apps.get_model("orders", "Order") # نسخهی تاریخی مدل، نه import مستقیم
batch = []
for order in Order.objects.filter(tracking_code="").only("pk").iterator(chunk_size=2000):
order.tracking_code = f"KSH{order.pk:07d}"
batch.append(order)
if len(batch) >= 2000:
Order.objects.bulk_update(batch, ["tracking_code"])
batch.clear()
Order.objects.bulk_update(batch, ["tracking_code"])
class Migration(migrations.Migration):
dependencies = [("orders", "0006_order_tracking_code")]
operations = [
migrations.RunPython(fill_codes, migrations.RunPython.noop),
]
آرگومان دوم (noop) اجازه میدهد migration برگشتپذیر بماند؛ بدون آن migrate orders 0005 با خطای «irreversible» متوقف میشود.
تداخل migration در کار تیمی
اگر شما و همکارتان هر دو روی شاخهی خودتان 0007_... ساخته باشید، بعد از merge پیام «Conflicting migrations detected» میگیرید. راهحل: python manage.py makemigrations --merge که یک migration ادغام با دو وابستگی میسازد. اگر هر دو migration یک فیلد را تغییر دادهاند، قبل از merge یکی را حذف و دوباره بسازید.
نکتههایی که کمتر کسی میداند
- در RunPython هرگز مدل را مستقیم import نکنید؛ مدل امروز ممکن است فیلدهایی داشته باشد که در آن نقطه از تاریخ هنوز وجود ندارند. همیشه
apps.get_model. - متدهای سفارشی مدل و متد
save()بازنویسیشده در مدل تاریخی وجود ندارند؛ فقط فیلدها و Managerهایی کهuse_in_migrations = Trueدارند. makemigrations --check --dry-runدر CI اگر مدلی تغییر کرده اما migration ساخته نشده باشد با کد خطا خارج میشود؛ جلوی «فراموش کردم migration بسازم» را میگیرد.- افزودن فیلد NOT NULL به جدول بزرگ را سهمرحلهای انجام دهید: اول فیلد nullable، بعد data migration، بعد NOT NULL کردن؛ تا جدول مدت طولانی قفل نماند.
- روی PostgreSQL هر migration در یک تراکنش اجرا میشود؛ برای ساختن ایندکس بدون قفل از
AddIndexConcurrentlyباatomic = Falseاستفاده کنید.