Data migration dùng operation RunPython, và phải lấy model qua apps.get_model() chứ không import từ models.py.
from django.db import migrations
def backfill_slug(apps, schema_editor):
Article = apps.get_model('blog', 'Article') # historical version of the model
for article in Article.objects.filter(slug='').iterator(chunk_size=2000):
article.slug = slugify(article.title)
article.save(update_fields=['slug'])
class Migration(migrations.Migration):
dependencies = [('blog', '0007_article_slug')]
operations = [migrations.RunPython(backfill_slug, migrations.RunPython.noop)]Vì sao không import trực tiếp: migration chạy trên bảng ở trạng thái lịch sử của thời điểm đó, còn models.py là phiên bản mới nhất. Nếu sau này bạn xoá một field, migration cũ import model trực tiếp sẽ tham chiếu field không còn tồn tại và vỡ khi ai đó dựng lại DB từ đầu. apps.get_model() trả về model được dựng lại đúng theo state của migration.
Ba lưu ý còn lại:
- Truyền RunPython.noop (hoặc hàm reverse thật) làm tham số thứ hai để migration còn rollback được.
- Model lịch sử không có custom method, không có signal, không có save() override — chỉ có field. Logic cần thiết phải viết lại trong migration.
- Bảng lớn thì xử lý theo lô (iterator() + bulk_update), tránh giữ transaction quá lâu.