Bir modeli ve ilişki alanlarını yeniden adlandırmak için Django taşıma stratejisi


153

Yeniden adlandırmak istediğim modellerle yabancı anahtar ilişkileri olan birçok modelin bulunduğu mevcut bir Django projesinde birkaç modeli yeniden adlandırmayı planlıyorum. Bunun birden fazla taşıma gerektireceğinden oldukça eminim, ancak tam prosedürden emin değilim.

Diyelim ki bir Django uygulamasında aşağıdaki modellerle başlıyorum myapp:

class Foo(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_ridonkulous = models.BooleanField()

FooModeli gerçekten yeniden adlandırmak istiyorum çünkü isim gerçekten mantıklı değil ve kodda karışıklığa neden oluyor ve Barçok daha net bir isim için.

Django geliştirme belgelerinde okuduklarımdan, aşağıdaki taşıma stratejisini varsayıyorum:

Aşama 1

Değiştir models.py:

class Bar(models.Model):  # <-- changed model name
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    foo = models.ForeignKey(Bar)  # <-- changed relation, but not field name
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Bar)  # <-- changed relation, but not field name
    is_ridonkulous = models.BooleanField()

İçin AnotherModelalan adının foodeğişmediğini, ancak ilişkinin Barmodele güncellendiğini unutmayın . Benim gerekçem, bir kerede çok fazla değişmemem ve bu alan adını olarak değiştirirsem baro sütundaki verileri kaybetme riskiyle karşı karşıya olduğum.

Adım 2

Boş bir taşıma oluşturun:

python manage.py makemigrations --empty myapp

Aşama 3

Düzenleme Migrationeklemek için 2. adımda oluşturduğunuz geçiş dosyasındaki sınıf RenameModeloperasyonlar listesine operasyonu:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'),
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar')
    ]

4. Adım

Taşıma işlemini uygulayın:

python manage.py migrate

Adım 5

İlgili alan adlarını şurada düzenleyin models.py:

class Bar(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_ridonkulous = models.BooleanField()

6. Adım

Başka bir boş taşıma oluşturun:

python manage.py makemigrations --empty myapp

Adım 7

Düzenleme Migration6. adımda oluşturulan geçiş dosyasındaki sınıf eklemek için RenameFieldoperasyon listesine ilgili herhangi alan adları için operasyon (ler):

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0002_rename_fields'),  # <-- is this okay?
    ]

    operations = [
        migrations.RenameField('AnotherModel', 'foo', 'bar'),
        migrations.RenameField('YetAnotherModel', 'foo', 'bar')
    ]

8. Adım

2. geçişi uygulayın:

python manage.py migrate

Kodun geri kalanını (görünümler, formlar, vb.) Yeni değişken adlarını yansıtacak şekilde güncellemenin yanı sıra, temel olarak yeni taşıma işlevselliği nasıl çalışır?

Ayrıca, bu birçok adım gibi görünüyor. Göç operasyonları bir şekilde yoğunlaştırılabilir mi?

Teşekkürler!

Yanıtlar:


127

Yani bunu denediğimde, Adım 3 - 7'yi yoğunlaştırabilirsiniz:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'), 
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar'),
        migrations.RenameField('AnotherModel', 'foo', 'bar'),
        migrations.RenameField('YetAnotherModel', 'foo', 'bar')
    ]

Yönetici.py ve hatta daha eski geçiş dosyaları (!) Gibi içeri aktarıldığı adları güncellemezseniz bazı hatalar alabilirsiniz.

Güncelleme : Ceasaro'nun belirttiği gibi, Django'nun daha yeni sürümleri genellikle bir modelin yeniden adlandırılıp adlandırılmadığını tespit edebilir ve sorabilir. Bu yüzden manage.py makemigrationsönce deneyin ve ardından taşıma dosyasını kontrol edin.


Cevap için teşekkürler. O zamandan beri özetlediğim adımları kullanarak taşındım, ancak bunu mevcut verilerle mi yoksa yalnızca boş bir veritabanıyla mı denediğinizi merak ediyorum?
Fiver

2
Mevcut verilerle denedim, yerel envimdeki sqlite üzerinde birkaç satır olsa da (
Üretim'e taşındığımda,

4
Geçiş dosyalarında kullanıyorsanız apps.get_model, geçiş dosyalarındaki model adını değiştirmeniz gerekmez. bunu çözmem çok zaman aldı.
Ahmed

9
Model adınızı değiştirirseniz django 2.0'da ./manage.py makemigrations myappkomut, modelinizi yeniden adlandırıp adlandırmamanızı ister. Örneğin: myapp.Foo modelini Bar olarak yeniden adlandırdınız mı? [y / N] 'y' yanıtını verirseniz taşıma işleminiz migration.RenameModel('Foo', 'Bar'), yeniden adlandırılan alanlar için aynı sayıları içerecektir :-)
ceasaro

1
manage.py makemigrations myappyine de başarısız olabilir: "Modelin adını ve alanlarının birçoğunu aynı anda değiştirirseniz bunu manuel olarak eklemeniz gerekebilir; otomatik algılayıcıya, bu eski ada sahip bir modeli sildiğiniz ve farklı bir ad ve oluşturduğu taşıma eski tablodaki tüm verileri kaybeder. " Django 2.1 Docs Benim için boş bir geçiş oluşturmak, model adlarını ona eklemek, sonra makemigrationsher zamanki gibi çalıştırmak yeterliydi .
hlongmore

37

İlk başta, Fiver'in yönteminin benim için işe yaradığını düşündüm çünkü geçiş 4. adıma kadar iyi çalıştı. Bu nedenle ilişki alanlarını yeniden adlandırmak istediğimde taşıma başarısız oldu (adım 5-8). Bu benim durumumdaki diğer uygulamalarda 'AnotherModel' ve 'YetAnotherModel' öğelerinin gönderilmesinden kaynaklanıyor olabilir.

Bu yüzden aşağıdaki adımları uygulayarak modellerimi ve ilişki alanlarımı yeniden adlandırmayı başardım:

İ yöntemi uyarlanmıştır , bu ve otranzer özellikle hile.

Fiver izin Yani böyle en İçinde var ki Uygulamam :

class Foo(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)

Ve myotherapp'ta :

class AnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_ridonkulous = models.BooleanField()

Aşama 1:

Her OneToOneField (Foo) veya ForeignKeyField (Foo) 'yu IntegerField ()' a dönüştürün. (Bu, ilgili Foo nesnesinin kimliğini tamsayı alanının değeri olarak tutar).

class AnotherModel(models.Model):
    foo = models.IntegerField()
    is_awesome = models.BooleanField()

class YetAnotherModel(models.Model):
    foo = models.IntegerField()
    is_ridonkulous = models.BooleanField()

Sonra

python manage.py makemigrations

python manage.py migrate

Adım 2: (Fiver'dan 2-4. Adım gibi)

Model adını değiştirme

class Bar(models.Model):  # <-- changed model name
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)

Boş bir taşıma oluşturun:

python manage.py makemigrations --empty myapp

Ardından şu şekilde düzenleyin:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'),
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar')
    ]

Sonuçta

python manage.py migrate

Aşama 3:

IntegerField () öğenizi önceki ForeignKeyField veya OneToOneField öğelerine, ancak yeni Bar Modeliyle geri dönüştürün. (Önceki tamsayı alanı kimliği saklıyordu, bu yüzden django bunu anlıyor ve serin olan bağlantıyı yeniden kuruyor.)

class AnotherModel(models.Model):
    foo = models.ForeignKey(Bar)
    is_awesome = models.BooleanField()

class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Bar)
    is_ridonkulous = models.BooleanField()

Sonra şunları yapın:

python manage.py makemigrations 

Çok önemlisi, bu adımda her yeni geçişi değiştirmeniz ve RenameModel Foo-> Bar geçişlerine bağımlılığı eklemeniz gerekir. Yani hem AnotherModel hem de YetAnotherModel myotherapp içindeyse, myotherapp'ta oluşturulan taşıma şöyle görünmelidir:

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '00XX_the_migration_of_myapp_with_renamemodel_foo_bar'),
        ('myotherapp', '00xx_the_migration_of_myotherapp_with_integerfield'),
    ]

    operations = [
        migrations.AlterField(
            model_name='anothermodel',
            name='foo',
            field=models.ForeignKey(to='myapp.Bar'),
        ),
        migrations.AlterField(
            model_name='yetanothermodel',
            name='foo',
            field=models.ForeignKey(to='myapp.Bar')
        ),
    ]

Sonra

python manage.py migrate

4. Adım:

Sonunda alanlarınızı yeniden adlandırabilirsiniz

class AnotherModel(models.Model):
    bar = models.ForeignKey(Bar) <------- Renamed fields
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    bar = models.ForeignKey(Bar) <------- Renamed fields
    is_ridonkulous = models.BooleanField()

ve sonra otomatik yeniden adlandırma yapın

python manage.py makemigrations

(django, model adını gerçekten değiştirip değiştirmediğinizi sormalıdır, evet deyin)

python manage.py migrate

Ve bu kadar!

Bu Django1.8 üzerinde çalışıyor


3
Teşekkür ederim! Bu son derece yardımcı oldu. Ama bir not - PostgreSQL alan indekslerini de el ile yeniden adlandırmam ve / veya kaldırmam gerekti çünkü Foo'yu Bar olarak yeniden adlandırdıktan sonra Bar adında yeni bir model oluşturdum.
Anatoly Scherbakov

Bunun için teşekkür ederim! Bence anahtar kısım, yeniden adlandırılacak modelin içindeki veya dışındaki tüm yabancı anahtarları 'e dönüştürüyor IntegerField. Bu benim için mükemmel çalıştı ve doğru adla yeniden yaratılma avantajına sahip. Doğal olarak, onları çalıştırmadan önce tüm göçleri gözden geçirmenizi tavsiye ederim!
zelanix

Teşekkür ederim! Diğer modellerin yabancı anahtarlara sahip olduğu bir modeli (1-3 arası adımlar) yeniden adlandırmak için birçok farklı strateji denedim ve bu işe yarayan tek şeydi.
MSH

Bugün günümü kurtarmak ForeignKeyiçin IntegerFields değiştirme !
mehmet

8

Aynı şeyi yapmam ve takip etmem gerekiyordu. Modeli bir kerede değiştirdim (Adım 1 ve 5, Fiver'in cevabından birlikte). Sonra bir şema geçişi oluşturdu, ancak bunu şu şekilde düzenledi:

class Migration(SchemaMigration):
    def forwards(self, orm):
        db.rename_table('Foo','Bar')

    def backwards(self, orm):
        db.rename_table('Bar','Foo')

Bu mükemmel çalıştı. Mevcut tüm verilerim ortaya çıktı, diğer tüm tablolar Bar fine'i referans aldı.

buradan: https://hanmir.wordpress.com/2012/08/30/rename-model-django-south-migration/


Harika, paylaştığın için teşekkürler. Bu cevap yardımcı olduysa + 1 wasibigeek emin olun.
Fiver

7

Django 1.10 için, sadece Makemigrations'ı çalıştırarak iki model sınıfı adını (ForeignKey dahil ve verilerle) değiştirmeyi ve ardından uygulama için taşımayı başardım. Makemigrations adımı için tablo adlarını değiştirmek istediğimi onaylamam gerekiyordu. Migrate tabloların adlarını sorunsuz değiştirdi.

Sonra ForeignKey alanının adını eşleştirmek için değiştirdim ve Makemigrations tarafından adı değiştirmek istediğimi tekrar onaylamam istendi. Değişikliği yapmaktan daha fazla göç edin.

Bu yüzden bunu özel bir dosya düzenlemesi olmadan iki adımda attım. İlk başta hatalar aldım çünkü admin.py dosyasını değiştirmeyi unuttum, @wasibigeek tarafından belirtildiği gibi.


Çok teşekkürler! Django 1.11 için de mükemmel
Francisco

6

Ayrıca, v.thorey tarif ettiği gibi problemle de karşılaştım ve yaklaşımının çok yararlı olduğunu buldum, ancak Fiver'in adım 1 ila 4 olmadan tarif ettiği gibi aslında adım 5 ila 8'e kadar daha az aşamaya yoğunlaştırılabildiğini, ancak adım 7'nin benim adım 3'ün altındadır. Genel adımlar aşağıdaki gibidir:

Adım 1: Models.py'de ilgili alan adlarını düzenleyin

class Bar(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)


class AnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    bar = models.ForeignKey(Bar)  # <-- changed field name
    is_ridonkulous = models.BooleanField()

2. Adım: Boş bir taşıma oluşturma

python manage.py makemigrations --empty myapp

Adım 3: Adım 2'de oluşturulan geçiş dosyasındaki Migration sınıfını düzenleyin

class Migration(migrations.Migration):

dependencies = [
    ('myapp', '0001_initial'), 
]

operations = [
    migrations.AlterField(
        model_name='AnotherModel',
        name='foo',
        field=models.IntegerField(),
    ),
    migrations.AlterField(
        model_name='YetAnotherModel',
        name='foo',
        field=models.IntegerField(),
    ),
    migrations.RenameModel('Foo', 'Bar'),
    migrations.AlterField(
        model_name='AnotherModel',
        name='foo',
        field=models.ForeignKey(to='myapp.Bar'),
    ),
    migrations.AlterField(
        model_name='YetAnotherModel',
        name='foo',
        field=models.ForeignKey(to='myapp.Bar'),
    ),
    migrations.RenameField('AnotherModel', 'foo', 'bar'),
    migrations.RenameField('YetAnotherModel', 'foo', 'bar')
]

4. Adım: Taşıma işlemini uygulayın

python manage.py migrate

Bitti

PS Bu yaklaşımı Django 1.9 üzerinde denedim


5

Django 1.9.4 sürümünü kullanıyorum

Aşağıdaki adımları izledim: -

Ben sadece oldName modelini NewName Run olarak yeniden adlandırdım python manage.py makemigrations. Sizden Did you rename the appname.oldName model to NewName? [y/N]Y seçmeniz istenecek

Koş python manage.py migrateve senden isteyecek

Aşağıdaki içerik türleri eskidir ve silinmeleri gerekir:

appname | oldName
appname | NewName

Yabancı bir anahtarla bu içerik türleriyle ilgili nesneler de silinir. Bu içerik türlerini silmek istediğinizden emin misiniz? Emin değilseniz, 'hayır' cevabını verin.

Type 'yes' to continue, or 'no' to cancel: Select No

Mevcut tüm verileri yeniden adlandırır ve benim için yeni adlandırılmış tabloya taşır.


Teşekkürler ahbap, kafam karıştı çünkü "hayır" vurduktan sonra hiçbir şey olmadı
farhawa

3

Ne yazık ki, veritabanında eski tablo adlarını bırakan yeniden adlandırma geçişi ile ilgili sorunlar (her django 1.x) buldum.

Django eski masada hiçbir şey denemiyor, sadece kendi modelini yeniden adlandırıyor. Yabancı anahtarlar ve genel olarak endeksler ile aynı sorun - orada değişiklikler Django tarafından düzgün izlenmemektedir.

En basit çözüm (geçici çözüm):

class Foo(models.Model):
     name = models.CharField(unique=True, max_length=32)
     ...
Bar = Foo  # and use Bar only

Gerçek çözüm (ziyade 2 kaydedilmesini vb tüm endeksler, kısıtları, tetikleyiciler, isimleri, geçiş için kolay bir yol daha küçük tablolar):

A taahhüdü:

  1. eskisi ile aynı modeli yarat
# deprecated - TODO: TO BE REMOVED
class Foo(model.Model):
    ...

class Bar(model.Model):
    ...
  1. kodu Baryalnızca yeni modelle çalışacak şekilde değiştirin . (şemadaki tüm ilişkiler dahil)

Taşımada hazırlamak RunPython(dahil Bar Foo verileri kopyalamak, hangi idFoo)

  1. isteğe bağlı optimizasyon (daha büyük tablolar için gerekirse)

Taahhüt B: (acele etmeyin, tüm bir ekip taşındığında yapın)

  1. eski modelin güvenli damlası Foo

daha fazla temizlik:

  • göçmenlik

Django hata:


3

Sadece onaylamak ve ceasaro yorum üzerine eklemek istedim. Django 2.0 bunu şimdi otomatik olarak yapıyor gibi görünüyor.

Django 2.2.1'deyim, modeli yeniden adlandırmak ve çalıştırmak için ne yapmam gerekiyordu makemigrations .

İşte belirli bir sınıf adını olsaydı sorar Aiçin B, ben evet ve ran göçünü seçti ve bütün işler gibi görünüyor.

Not Proje / taşıma klasörü içindeki hiçbir dosyada eski model adını yeniden adlandırmadım.


1

Birkaç tabloyu yeniden adlandırmam gerekiyordu. Ancak Django tarafından sadece bir model yeniden adlandırma fark edildi. Bu, Django'nun tekrar tekrar eklenmesi ve ardından modellerin kaldırılması nedeniyle oldu. Her çift için aynı uygulamada olup olmadıklarını ve aynı alanlara sahip olup olmadıklarını kontrol eder . Yalnızca bir tablonun yeniden adlandırılacak tablolara yabancı anahtarı yoktu (hatırladığınız gibi yabancı anahtarlar model sınıfı adını içerir). Başka bir deyişle, yalnızca bir tabloda alan değişikliği yoktu. Bu yüzden fark edildi.

Dolayısıyla çözüm, her seferinde bir tabloyu yeniden adlandırmak models.py, muhtemelen model sınıfı adını değiştirmek views.pyve bir geçiş yapmaktır. Bundan sonra kodunuzu diğer referanslar (model sınıfı adları, ilgili (sorgu) adları, değişken adları) açısından inceleyin. Gerekirse bir geçiş yapın. Ardından, isteğe bağlı olarak tüm bu taşıma işlemlerini tek bir dosyada birleştirin (içe aktarmaları da kopyaladığınızdan emin olun).


1

Bu cevap üzerine yaptığı yorumda @ceasaro kelimeleri yapacağım .

Django'nun yeni sürümleri değişiklikleri algılayabilir ve ne yapıldığını sorabilir. Ayrıca, Django'nun bazı geçiş komutlarının yürütme sırasını karıştırabileceğini de ekleyeceğim.

Küçük değişiklikleri uygulamak ve çalıştırmak için akıllıca olacaktır makemigrationsve migrateyanılma oluşursa göç dosyası düzenlenebilir.

Hatalardan kaçınmak için bazı satırların yürütme sırası değiştirilebilir.


Model adlarını değiştirirseniz ve tanımlanan yabancı anahtarlar varsa, bunun işe yaramadığını unutmayın ...
Dean Kayton

Önceki yoruma göre genişletme: Yaptığım tek şey model adlarını değiştirmek ve makemigrations çalıştırmaksa yabancı anahtarlarda vb. 'NameError: name' <oldmodel> 'tanımlı değil' mesajı alıyorum ... Bunu değiştirir ve makemigrations çalıştırırım admin.py ... bunu düzeltir ve tekrar makemigrations çalıştırırsam, '<app.oldmodel> modelini <newmodel>' olarak yeniden adlandırdınız mı? Ancak daha sonra taşıma işlemlerinde 'ValueError: The area <app .newmodel.field1> '<app.oldmodel>' için tembel bir referansla açıklandı, ancak '<app>' uygulaması '<oldmodel>' vb. '' modelini sağlamıyor
Dean Kayton

Bu hata, geçmiş taşıma işlemlerinizdeki referansları yeniden adlandırmanız gerektiği anlaşılıyor.
mhatch

@DeanKayton bunun migrations.SeparateDatabaseAndStateyardımcı olabileceğini söyleyebilir mi?
diogosimao

1

PyCharm gibi iyi bir IDE kullanıyorsanız, model adına sağ tıklayıp bir refactor -> yeniden adlandır yapabilirsiniz. Bu, modele başvuran tüm kodlarınızı gözden geçirme zahmetinden kurtarır. Sonra makgrasyonları çalıştırın ve taşıyın. Django 2+ sadece ad değişikliğini onaylayacaktır.


-10

Django'yu sürüm 10'dan sürüm 11'e yükselttim:

sudo pip install -U Django

( -U"yükseltme" için) ve sorunu çözdü.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.