Custom django migration yazmak

Django migrationlar django’nun beni kendine hayran birakan ozelliklerinden biri oldugunu cok rahat soyleyebilirim.

Bazi zamanlar oluyorki modellerde yaptigimiz degisikliklerden oturu bir migration olusturdugumuzda bu migrationa yeni eklemeler yapmamiz gerekebiliyor.

Bu yazimda basit bir ornek uzerinden Django’da custom migration nasil yazilir ondan bahsetmeye calisacagim.

amount adinda bir zorunlu(null=False) fielda sahip olan bir modelimiz var diyelim ve biz bu modele invoice_amount adinda zorunlu ikinci bir field ekledik diyelim. Ekledigimiz bu yeni fieldin db’de olusturulmasi icin makemigrations yaptigimizda django bizden tablomuzda hali hazirda bulunan kayitlarin invoice_amount fieldina yazilmak uzere default bir deger isteyecektir. Diyelimki biz bu yeni olusturulacak fielda default deger olarak amount fieldinda yazan degeri yazmak istiyoruz. Bunu olusan migration dosyasina kucuk bir ekleme yaparak basarabiliriz.

Ilk olarak migration dosyamizi olustururken invoice_amount fieldi icin default value istediginde tablomuzda olmayan bir default value veriyoruz. Mesela ben 111222333 verdim.

Olusan migration dosyam su sekilde:

# -*- coding: utf-8 -*-

from __future__ import unicode_literals
from django.db import migrations, models

class Migration(migrations.Migration):
    dependencies = []

    operations = [
        migrations.AddField(
            model_name='mymodel',
            name='invoice_amount',
            field=models.DecimalField(decimal_places=2, default=111222333, max_digits=15),
            preserve_default=False,
        ),
    ]

Bu migration’i migrate ettigimizde ilgili tabloya invoice_amount fieldini ekleyecek ve tablodaki kayitlarin invoice_amount degerlerinide 111222333 olarak set edecektir ama biz yeni olusturulan fieldini amount fieldina esitlemek istiyoruz. Bunu yapmanin iki yolu var.

RunSQL kullanmak

RunSQL migration icerisinde custom sql calistirmamiza olanak saglar.

Mesela su sekil bir degisiklik ile invoice_amount fieldini amount fieldina esitleyebiliyoruz:

    operations = [
        migrations.AddField(
            model_name='mymodel',
            name='invoice_amount',
            field=models.DecimalField(decimal_places=2, default=111222333, max_digits=15),
            preserve_default=False,
        ),

        // Sonradan ekledigimiz kisim
        migrations.RunSQL(["UPDATE myapp_mymodel set invoice_amount=amount where invoice_amount=111222333"],  migrations.RunSQL.noop),
    ],    

Bu migrationi calistirdigimizda field olusturulacak, tabloda varolan kayitlarininvoice_amount field valuelari 111222333 olarak set edilecek. Daha sonra ise invoice_amount fielina amount fieldinin degerini set eden SQL calistirilacak.

RunPython kullanmak

RunPython ile SQL yerine fonksiyon calistirabiliyoruz.

from __future__ import unicode_literals
from django.db import migrations, models
from django.db.models import F

def forwards_func(apps, schema_editor):
    # migration calistiginda calisir
    Mymodel = apps.get_model('myapp', 'Mymodel')
    Mymodel.objects.update(invoice_amount=F('amount'))

def reverse_func(apps, schema_editor):
    # migration'i geri aldigimizda calisir.
    pass

class Migration(migrations.Migration):
    dependencies = []

    operations = [
        migrations.AddField(
            model_name='mymodel',
            name='invoice_amount',
            field=models.DecimalField(decimal_places=2, default=111222333, max_digits=15, blank=True),
            preserve_default=False,
        ),
        migrations.RunPython(forwards_func, reverse_func),
    ]

Ihtiyaciniza gore RunPython veyada RunSQL kullanabilirsiniz.

Kaynaklar:

comments powered by Disqus