Bir modeli iki Django uygulaması arasında taşıma (Django 1.7)


133

Yaklaşık bir yıl önce bir projeye başladım ve tüm yeni geliştiriciler gibi ben de yapıya çok fazla odaklanmadım, ancak şimdi Django ile daha da iyiyim, proje düzenimin esas olarak modellerimin yapısı korkunç gibi görünmeye başladı .

Esas olarak tek bir uygulamada tutulan modellerim var ve bu modellerin çoğu kendi bireysel uygulamalarında olmalı, bunu denedim ve çözdüm ve onları güneye taşıdım ancak yabancı anahtarlar vb. Nedeniyle zor ve gerçekten zor buldum.

Ancak, Django 1.7 ve yerleşik geçiş desteği nedeniyle bunu şimdi yapmanın daha iyi bir yolu var mı?


4
Kabul edilen cevabı değiştirmeyi düşünebilirsiniz.
Babken Vardanyan

Gelecekte bununla karşılaşacak insanlar için: Django 3.x burada ve realpython.com/move-django-model/… adresinde ayrıntılı olarak açıklanan yaklaşım benim için çalıştı. Eski uygulamadaki modeller ve yeni uygulamadaki modeller arasında birden fazla yabancı anahtarım vardı.
pradeepcep

Yanıtlar:


16

Veri kaybına neden olabileceği için eski yanıtı kaldırıyorum. Gibi ozan sözü , biz 2 göçler her uygulamada tek oluşturabilir. Bu yazının altındaki yorumlar eski cevabıma atıfta bulunuyor.

Modeli 1. uygulamadan kaldırmak için ilk geçiş.

$ python manage.py makemigrations old_app --empty

Bu işlemleri içerecek şekilde taşıma dosyasını düzenleyin.

class Migration(migrations.Migration):

    database_operations = [migrations.AlterModelTable('TheModel', 'newapp_themodel')]

    state_operations = [migrations.DeleteModel('TheModel')]

    operations = [
      migrations.SeparateDatabaseAndState(
        database_operations=database_operations,
        state_operations=state_operations)
    ]

İlk geçişe bağlı olan ve 2. uygulamada yeni tabloyu oluşturan ikinci geçiş. Model kodunu 2. uygulamaya taşıdıktan sonra

$ python manage.py makemigrations new_app 

ve taşıma dosyasını buna benzer bir şekilde düzenleyin.

class Migration(migrations.Migration):

    dependencies = [
        ('old_app', 'above_migration')
    ]

    state_operations = [
        migrations.CreateModel(
            name='TheModel',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
            ],
            options={
                'db_table': 'newapp_themodel',
            },
            bases=(models.Model,),
        )
    ]

    operations = [
        migrations.SeparateDatabaseAndState(state_operations=state_operations)
    ]

Elimde mevcut verilerim var ve kaybedemeyeceğim bir sürü şey var, muhtemelen bununla mı yapmalı?
Sam Buckingham

@KevinChristopherHenry Kodu değiştirdi. Bu, mevcut verileri korur.
ChillarAnand

@SamBuckingham Evet, verileri kaybetmeden değiştirilmiş kodla geçiş yapmayı deneyebilirsiniz.
ChillarAnand

2
Bence bu gerçekten en iyi yol olacak, tüm yardımlar için teşekkürler çocuklar.
Sam Buckingham

1
IMO bu yanlış bir çözümdür, temel göç varsayımı, çalıştırırsanız ./manage.py migrateher şeyin iyi durumda biteceği şeklindedir. Taşıma işlemlerini manuel olarak taklit etmek IMO'nun yanlış bir yoludur.
jb.

341

Bu, kullanılarak oldukça kolay bir şekilde yapılabilir migrations.SeparateDatabaseAndState. Temel olarak, modeli bir uygulamanın geçmişinden kaldırmak ve başka bir uygulamada oluşturmak için iki durum işlemiyle eşzamanlı olarak tabloyu yeniden adlandırmak için bir veritabanı işlemi kullanırız.

Eski uygulamadan kaldır

python manage.py makemigrations old_app --empty

Göçte:

class Migration(migrations.Migration):

    dependencies = []

    database_operations = [
        migrations.AlterModelTable('TheModel', 'newapp_themodel')
    ]

    state_operations = [
        migrations.DeleteModel('TheModel')
    ]

    operations = [
        migrations.SeparateDatabaseAndState(
            database_operations=database_operations,
            state_operations=state_operations)
    ]

Yeni uygulamaya ekle

İlk olarak, modeli yeni uygulamanın model.py dosyasına kopyalayın, ardından:

python manage.py makemigrations new_app

Bu CreateModel, tek operasyon olarak saf bir operasyonla bir göç yaratacaktır . SeparateDatabaseAndStateMasayı yeniden oluşturmaya çalışmayacağımız bir operasyona sarın . Ayrıca önceki geçişi bir bağımlılık olarak dahil edin:

class Migration(migrations.Migration):

    dependencies = [
        ('old_app', 'above_migration')
    ]

    state_operations = [
        migrations.CreateModel(
            name='TheModel',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
            ],
            options={
                'db_table': 'newapp_themodel',
            },
            bases=(models.Model,),
        )
    ]

    operations = [
        migrations.SeparateDatabaseAndState(state_operations=state_operations)
    ]

14
Gerçekten iyi bir açıklama. Cevap bu olmalı, tabloyu yeniden adlandırarak herhangi bir veri kaybını önlemiş olursunuz.
Remiz

11
Bunu yapmanın en iyi yolu bu ve benimkinden çok daha iyi. Cevabımın üstüne not ekledim.
ChillarAnand

4
Bunu yaptım, ancak bundan sonra newapp üzerinde "makemigrations" çalıştırdığımda, onu None olarak yeniden adlandıran bir AlterModelTable geçişi oluşturuyor.
Diego Ponciano

4
Bu talimatlara göre sorunumu çözmenin bir yolunu buldum. Zorunlu alanlar olan yabancı anahtar referanslarınız varsa, sorun daha karmaşıktır. Referansları taşımak için birkaç adım eklemem gerekti.
Nostalg.io

14
Birden fazla istek nedeniyle, bir GitHub örneği ile FK model geçişlerine ayrıntılı bir cevap oluşturdum. stackoverflow.com/questions/30601107/…
Nostalg.io

26

Ben de aynı problemle karşılaştım. Ozan'ın cevabı bana çok yardımcı oldu ama maalesef yeterli olmadı. Aslında, taşımak istediğim modele bağlanan birkaç ForeignKey vardı. Biraz baş ağrısından sonra çözümü buldum, bu yüzden insanların zamanını çözmek için onu göndermeye karar verdim.

2 adıma daha ihtiyacınız var:

  1. Her şeyden önce, tüm değiştirmek ForeignKeyiçin bağlama TheModeliçine Integerfield. O zaman koşpython manage.py makemigrations
  2. Ozan'ın adımları yaptıktan sonra, yeniden dönüştürmek için yabancı anahtarları: geri koymak ForeignKey(TheModel)yerine IntegerField(). Ardından geçişleri tekrar yapın ( python manage.py makemigrations). Daha sonra taşıyabilirsiniz ve çalışmalıdır ( python manage.py migrate)

Umarım yardımcı olur. Elbette kötü sürprizlerden kaçınmak için üretimde denemeden önce yerel olarak test edin :)


8
ManyToManyField ilişkileri ne olacak?
tomcounsell

1
@tomcounsell harika bir yorum, yalnızca geçiş amacıyla belirli bir geçiş modeli ekleyerek varsayabilirim. Verileri olduğu gibi bırakmak için büyük bir çalışma gerekiyor ...
Wtower

Çoktan çoğa ilişki genellikle yalnızca iki yabancı anahtar içeren bir tablo olduğundan, SQL açısından bu cevabın püf noktasını uygulayabilirsiniz. Ancak bunu yalnızca Django aracılığıyla başarmak için, düşünebildiğim bir yaklaşım @ozan yanıtının satırları boyunca olabilir, ancak ilk adım MTM ilişkisinde yer alan tabloları kopyalamaktır (her uygulamadaki kopyaların bir sürümü) , tüm Yabancı Anahtarları yeni uygulamaya taşıyın ve ancak daha sonra eski uygulamadaki kopyaları silin. Feragatname: Test etmedim :)
Arnaud P

15

Nasıl yaptım (Django == 1.8'de postgres ile test edildi, yani muhtemelen 1.7)

Durum

app1.YourModel

ancak şuna gitmesini istiyorsunuz: app2.YourModel

  1. Modelinizi (kod) uygulama1'den uygulama2'ye kopyalayın.
  2. bunu app2.YourModel'e ekleyin:

    Class Meta:
        db_table = 'app1_yourmodel'
  3. $ python manage.py makemigrations app2

  4. App2'de migrations.CreateModel () ifadesiyle yeni bir migration (ör. 0009_auto_something.py) yapılır, bu ifadeyi app2'nin ilk geçişine taşıyın (ör. 0001_initial.py) (her zaman olduğu gibi olacaktır). Ve şimdi oluşturulan geçişi kaldırın = 0009_auto_something.py

  5. Tıpkı siz hareket ettiğiniz gibi, app2.YourModel her zaman oradaydı, şimdi app1.YourModel'in varlığını geçişlerinizden kaldırın. Anlamı: CreateModel deyimlerini ve bundan sonra kullandığınız her ayarlama veya veri değişimini yorumlayın.

  6. Ve tabii ki, app1.YourModel'e yapılan her referans, projeniz boyunca app2.YourModel olarak değiştirilmelidir. Ayrıca, taşıma işlemlerinde app1.YourModel'in tüm olası yabancı anahtarlarının app2.YourModel olarak değiştirilmesi gerektiğini unutmayın.

  7. Şimdi $ python manage.py migrate yaparsanız, hiçbir şey değişmez, ayrıca $ python manage.py makemigrations yaptığınızda da yeni bir şey tespit edilmemiştir.

  8. Şimdi son dokunuş: Class Meta'yı app2.YourModel'den kaldırın ve $ python manage.py makemigrations app2 && python migrate app2 yapın (bu geçişe bakarsanız bunun gibi bir şey göreceksiniz :)

        migrations.AlterModelTable(
        name='yourmodel',
        table=None,
    ),

tablo = Yok, varsayılan tablo adını alacağı anlamına gelir, bu durumda bu durumda app2_yourmodel olacaktır.

  1. BİTTİ, kaydedilen verilerle.

PS, geçiş sırasında content_type app1.yourmodel'in kaldırıldığını ve silinebileceğini görecektir. Buna evet diyebilirsiniz, ancak yalnızca kullanmazsanız. Söz konusu içerik türüne yönelik FK'lerin bozulmamış olmasına büyük ölçüde bağımlı olmanız durumunda, henüz evet veya hayır yanıtını vermeyin, ancak o zaman manuel olarak db'ye gidin ve contentype app2.yourmodel'i kaldırın ve contenttype app1'i yeniden adlandırın. yourmodel'den app2.yourmodel'e gidin ve ardından hayır olarak yanıtlayarak devam edin.


3
Bu çözüm kesinlikle @ ozan'dan daha "hilekar" olsa da ve kesinlikle daha fazla düzenlemeye ihtiyaç duysa da, benim için iyi çalıştı (ve taşıma işlemlerini düzenlemekte sorun yok - belgelere göre bunların düzenlenebilir olması gerekiyor).
pgcd

1
Muhtemelen app_label = 'app1'meta seçeneğini de kullanın .
Wtower

Genius! Bu, ForeignKey ilişkilerinde benim için harika çalıştı. Sanırım bu aynı zamanda ManyToMany alanları için de işe yarayacak.
Babken Vardanyan

1
Adımlarınızı takip ettim ancak uygulama1'e ait bazı modeldeki alan, taşınacak modelle (myModel) özyinelemeli ilişkisi olan bir Yabancı Anahtar'dan oluşuyor. field1 = models.ForeignKey('app1.myModel').Geçiş yaptığımda olduğu gibi , bunu belirten bir ValueError alıyorumfield1 was declared with a lazy reference to 'app1.myModel' but app 'app1' doesn't provide model 'MyModel'
Deesha

12

Gergin elle kodlama geçişleri yaşıyorum ( Ozan'ın cevabının gerektirdiği gibi ), bu nedenle, gerekli el kodlama miktarını en aza indirmek için Ozan ve Michael'ın stratejileri birleştiriliyor :

  1. Herhangi bir modeli taşımadan önce, çalıştırarak temiz bir temelde çalıştığınızdan emin olun makemigrations.
  2. Dan Modeli için kod Taşı app1içinapp2
  3. @Michael tarafından önerildiği db_tableüzere, "yeni" modeldeki Meta seçeneğini kullanarak yeni modeli eski veritabanı tablosuna yönlendiriyoruz :

    class Meta:
        db_table = 'app1_yourmodel'
  4. Çalıştır makemigrations. Bu, CreateModeliçinde app2ve DeleteModeliçinde üretecektir app1. Teknik olarak, bu geçişler tam olarak aynı tabloya başvurur ve tabloyu kaldırır (tüm veriler dahil) ve yeniden oluşturur.

  5. Gerçekte, masaya bir şey yapmak istemiyoruz (veya buna ihtiyacımız yok). Sadece değişikliğin yapıldığına inanmak için Django'ya ihtiyacımız var. Per @ Ozan'ın cevabı, state_operationsbayrak SeparateDatabaseAndStatebunu yapıyor. Bu nedenle, tüm migrationsgirişleri İKİ GÖÇ DOSYALARINDA da yazıyoruzSeparateDatabaseAndState(state_operations=[...]) . Örneğin,

    operations = [
        ...
        migrations.DeleteModel(
            name='YourModel',
        ),
        ...
    ]

    olur

    operations = [
        migrations.SeparateDatabaseAndState(state_operations=[
            ...
            migrations.DeleteModel(
                name='YourModel',
            ),
            ...
        ])
    ]
  6. Ayrıca, yeni "sanal" CreateModelgeçişin , orijinal tabloyu gerçekten oluşturan veya değiştiren herhangi bir geçişe bağlı olduğundan emin olmanız gerekir . Örneğin, yeni geçişleriniz app2.migrations.0004_auto_<date>(için Create) ve app1.migrations.0007_auto_<date>(için Delete) ise, yapılacak en basit şey şudur:

    • Bağımlılığını açın app1.migrations.0007_auto_<date>ve kopyalayın app1(örneğin ('app1', '0006...'),). Bu, "hemen önceki" geçiştir app1ve tüm gerçek model oluşturma mantığına bağımlılıkları içermelidir.
    • app2.migrations.0004_auto_<date>Yeni kopyaladığınız bağımlılığı açın ve dependencieslistesine ekleyin .

Taşıdığınız ForeignKeymodelle ilişkiniz varsa , yukarıdakiler çalışmayabilir. Bunun nedeni:

  • ForeignKeyDeğişiklikler için bağımlılıklar otomatik olarak oluşturulmaz
  • ForeignKeyDeğişiklikleri sarmak istemiyoruz , state_operationsbu yüzden tablo işlemlerinden ayrı olduklarından emin olmamız gerekiyor.

NOT: Django 2.2 models.E028, bu yöntemi bozan bir uyarı ( ) ekledi . Bununla uğraşabilirsiniz managed=Falseama ben test etmedim.

"Minimum" işlem kümesi duruma bağlı olarak farklılık gösterir, ancak aşağıdaki yordam çoğu / tüm ForeignKeygeçişler için çalışmalıdır :

  1. KOPYA gelen modeli app1için app2seti, db_tableancak DO herhangi FK başvuruları değiştirmek DEĞİL.
  2. makemigrationsTüm app2taşımayı çalıştırın ve sarmalayın state_operations(yukarıya bakın)
    • Yukarıdaki gibi, app2 CreateTableen son app1geçişe bir bağımlılık ekleyin
  3. Tüm FK referanslarını yeni modele yönlendirin. Dize referansları kullanmıyorsanız, eski modeli models.py(KALDIRMAYIN) öğesinin altına taşıyın, böylece içe aktarılan sınıfla rekabet etmez.
  4. Çalıştır makemigrationsama hiçbir şeyi sarmayın state_operations(FK değişiklikleri gerçekten gerçekleşmelidir). Sonuç olarak bir bağımlılık ekleyin ForeignKeygöçler (yani AlterFieldkadar) CreateTableiçinde göç app2(bunu bunları takip sonraki adım için bu listeyi gerekir). Örneğin:

    • İçeren göç bulun CreateModelörneğin app2.migrations.0002_auto_<date>ve bu göçün adını kopyalayın.
    • Bu modele bir ForeignKey sahip olan tüm geçişleri bulun (örneğin, aşağıdaki app2.YourModelgibi geçişleri bulmak için arayarak) :

      class Migration(migrations.Migration):
      
          dependencies = [
              ('otherapp', '0001_initial'),
          ]
      
          operations = [
              migrations.AlterField(
                  model_name='relatedmodel',
                  name='fieldname',
                  field=models.ForeignKey(... to='app2.YourModel'),
              ),
          ]
    • Taşıma işlemini CreateModelbağımlılık olarak ekleyin :

      class Migration(migrations.Migration):
      
          dependencies = [
              ('otherapp', '0001_initial'),
              ('app2', '0002_auto_<date>'),
          ]  
  5. Modelleri buradan kaldırın app1

  6. Run makemigrationsve sarın app1içinde göç state_operations.
    • Tümüne bir bağımlılık ekleyin ForeignKeygöçler (yani AlterFieldbir önceki aşamadan) (içinde göçler içerebilir app1ve app2).
    • Bu geçişleri oluşturduğumda, DeleteTablezaten geçişlere bağlıydı, bu AlterFieldyüzden manuel olarak (yani daha Alterönce Delete) zorlamama gerek yoktu .

Bu noktada, Django gitmek iyidir. Yeni model eski tabloya işaret ediyor ve Django'nun göçleri onu her şeyin uygun şekilde yeniden yerleştirildiğine ikna etti. Büyük uyarı (@ Michael'ın cevabından) ContentTypeyeni model için yeni bir modelin yaratılmasıdır. ForeignKeyİçerik türlerine bağlarsanız (örneğin ile ), ContentTypetabloyu güncellemek için bir geçiş oluşturmanız gerekir .

Kendimden sonra temizlemek istedim (Meta seçenekleri ve tablo adları) bu nedenle aşağıdaki prosedürü kullandım (@Michael'den):

  1. db_tableMeta girişini kaldırın
  2. makemigrationsVeritabanını yeniden adlandırmak için tekrar çalıştırın
  3. Bu son geçişi düzenleyin ve DeleteTablegeçişe bağlı olduğundan emin olun . DeleteTamamen mantıklı olması gerektiği için gerekli gibi görünmüyor , ancak app1_yourmodelyapmazsam hatalarla karşılaşıyorum (örneğin yok).

Bu mükemmel çalıştı, teşekkürler! Bağımlılık ağacının en altında olduğu için son geçişi düzenlemenin önemli olduğunu düşünmüyorum.
James Meakin

1
İyi cevap! Geçişlere bir kapanış parantezi eklemeniz gerektiğini düşünüyorum.SeparateDatabaseAndState, değil mi?
atm

Bu benim için çalıştı. Ayrıca @JamesMeakin gibi son geçişi (3. adım, tüm cevabın son satırı) düzenlemedim ve yine de iyi çalıştı
Megawatt

ikinci senaryoda, FK'li olan ikinci adım, mantıklı bir hatayla benim için başarısız oldu:table_name: (models.E028) db_table 'table_name' is used by multiple models: app1.Model, app2.Model.
Mihai Zamfir

Prosedürü birkaç kez kullandım. 2.2 ( docs.djangoproject.com/en/2.2/ref/checks ) ve 2.1 ( docs.djangoproject.com/en/2.1/ref/checks ) için belgeleri karşılaştırırsanız, 2.2'ye eklendiğini görebilirsiniz. Etrafta çalışmak mümkün olabilir managed=Falseama kontrol edecek yerim yok.
claytond

1

Veriler büyük veya çok karmaşık değilse, ancak yine de korunması önemliyse, başka bir hack alternatifi şudur:

  • Manage.py dumpdata kullanarak veri fikstürlerini alın
  • Değişiklikleri ilişkilendirmeden değişiklikleri ve geçişleri doğru şekilde modellemeye devam edin
  • Armatürleri eski model ve uygulama adlarından yenisine global olarak değiştirin
  • Manage.py loaddata kullanarak verileri yükleyin

1

Https://stackoverflow.com/a/47392970/8971048 adresindeki cevabımdan kopyalandı

Modeli taşımanız gerekiyorsa ve artık uygulamaya erişiminiz yoksa (veya erişimi istemiyorsanız), yeni bir Operasyon oluşturabilir ve yeni bir model oluşturmayı ancak taşınan model taşımazsa düşünebilirsiniz. var olmak.

Bu örnekte old_app'tan 'MyModel'i benimapp'a geçiriyorum.

class MigrateOrCreateTable(migrations.CreateModel):
    def __init__(self, source_table, dst_table, *args, **kwargs):
        super(MigrateOrCreateTable, self).__init__(*args, **kwargs)
        self.source_table = source_table
        self.dst_table = dst_table

    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        table_exists = self.source_table in schema_editor.connection.introspection.table_names()
        if table_exists:
            with schema_editor.connection.cursor() as cursor:
                cursor.execute("RENAME TABLE {} TO {};".format(self.source_table, self.dst_table))
        else:
            return super(MigrateOrCreateTable, self).database_forwards(app_label, schema_editor, from_state, to_state)


class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0002_some_migration'),
    ]

    operations = [
        MigrateOrCreateTable(
            source_table='old_app_mymodel',
            dst_table='myapp_mymodel',
            name='MyModel',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=18))
            ],
        ),
    ]

Lütfen aynı cevabı birden fazla soruya eklemeyin. En iyisini cevaplayın ve geri kalanını kopya olarak işaretleyin. Bkz . Birkaç soruya mükerrer yanıt eklemek kabul edilebilir mi?
Petter Friberg

0

Bu kabaca test edilmiştir, bu nedenle DB'nizi yedeklemeyi unutmayın !!!

Örneğin, iki uygulama var: src_appve dst_appmodeli ' MoveMeden' src_appe taşımak istiyoruz dst_app.

Her iki uygulama için boş geçişler oluşturun:

python manage.py makemigrations --empty src_app
python manage.py makemigrations --empty dst_app

Farz edelim ki, yeni göçler XXX1_src_app_newve önceki XXX1_dst_app_newen büyük göçler XXX0_src_app_oldve XXX0_dst_app_old.

MoveMeModel için tabloyu yeniden adlandıran ve ProjectState içindeki app_label öğesini yeniden adlandıran bir işlem ekleyin XXX1_dst_app_new. XXX0_src_app_oldGöç konusunda bağımlılık eklemeyi unutmayın . Ortaya çıkan XXX1_dst_app_newgöç:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations

# this operations is almost the same as RenameModel
# https://github.com/django/django/blob/1.7/django/db/migrations/operations/models.py#L104
class MoveModelFromOtherApp(migrations.operations.base.Operation):

    def __init__(self, name, old_app_label):
        self.name = name
        self.old_app_label = old_app_label

    def state_forwards(self, app_label, state):

        # Get all of the related objects we need to repoint
        apps = state.render(skip_cache=True)
        model = apps.get_model(self.old_app_label, self.name)
        related_objects = model._meta.get_all_related_objects()
        related_m2m_objects = model._meta.get_all_related_many_to_many_objects()
        # Rename the model
        state.models[app_label, self.name.lower()] = state.models.pop(
            (self.old_app_label, self.name.lower())
        )
        state.models[app_label, self.name.lower()].app_label = app_label
        for model_state in state.models.values():
            try:
                i = model_state.bases.index("%s.%s" % (self.old_app_label, self.name.lower()))
                model_state.bases = model_state.bases[:i] + ("%s.%s" % (app_label, self.name.lower()),) + model_state.bases[i+1:]
            except ValueError:
                pass
        # Repoint the FKs and M2Ms pointing to us
        for related_object in (related_objects + related_m2m_objects):
            # Use the new related key for self referential related objects.
            if related_object.model == model:
                related_key = (app_label, self.name.lower())
            else:
                related_key = (
                    related_object.model._meta.app_label,
                    related_object.model._meta.object_name.lower(),
                )
            new_fields = []
            for name, field in state.models[related_key].fields:
                if name == related_object.field.name:
                    field = field.clone()
                    field.rel.to = "%s.%s" % (app_label, self.name)
                new_fields.append((name, field))
            state.models[related_key].fields = new_fields

    def database_forwards(self, app_label, schema_editor, from_state, to_state):
        old_apps = from_state.render()
        new_apps = to_state.render()
        old_model = old_apps.get_model(self.old_app_label, self.name)
        new_model = new_apps.get_model(app_label, self.name)
        if self.allowed_to_migrate(schema_editor.connection.alias, new_model):
            # Move the main table
            schema_editor.alter_db_table(
                new_model,
                old_model._meta.db_table,
                new_model._meta.db_table,
            )
            # Alter the fields pointing to us
            related_objects = old_model._meta.get_all_related_objects()
            related_m2m_objects = old_model._meta.get_all_related_many_to_many_objects()
            for related_object in (related_objects + related_m2m_objects):
                if related_object.model == old_model:
                    model = new_model
                    related_key = (app_label, self.name.lower())
                else:
                    model = related_object.model
                    related_key = (
                        related_object.model._meta.app_label,
                        related_object.model._meta.object_name.lower(),
                    )
                to_field = new_apps.get_model(
                    *related_key
                )._meta.get_field_by_name(related_object.field.name)[0]
                schema_editor.alter_field(
                    model,
                    related_object.field,
                    to_field,
                )

    def database_backwards(self, app_label, schema_editor, from_state, to_state):
        self.old_app_label, app_label = app_label, self.old_app_label
        self.database_forwards(app_label, schema_editor, from_state, to_state)
        app_label, self.old_app_label = self.old_app_label, app_label

    def describe(self):
        return "Move %s from %s" % (self.name, self.old_app_label)


class Migration(migrations.Migration):

    dependencies = [
       ('dst_app', 'XXX0_dst_app_old'),
       ('src_app', 'XXX0_src_app_old'),
    ]

    operations = [
        MoveModelFromOtherApp('MoveMe', 'src_app'),
    ]

Bağımlılığını ekleyin XXX1_dst_app_newetmek XXX1_src_app_new. XXX1_src_app_newgelecekteki src_appgeçişlerin daha sonra yürütüleceğinden emin olmak için gerekli olan işlemsiz geçiştir XXX1_dst_app_new.

Taşı MoveMegelen src_app/models.pyetmek dst_app/models.py. O zaman koş:

python manage.py migrate

Bu kadar!


Bu kodun muhtemelen yalnızca django 1.7 için yararlı olduğunu unutmayın. Bunu django 2.0'da denemek işe yaramayacak. Bu aynı zamanda, modelleri taşımak için bu mekanizmayı kullanmanın django sürümünüzü yükseltmek için bakım ek yükü getirdiği anlamına gelir.
Paul in 't Hout

0

Aşağıdakileri deneyebilirsiniz (test edilmemiş):

  1. gelen modeli hareket src_appiçindest_app
  2. göç etmek dest_app; şema geçişinin en son src_appgeçişe bağlı olduğundan emin olun ( https://docs.djangoproject.com/en/dev/topics/migrations/#migration-files )
  3. dest_apptüm verileri kopyalayan bir veri geçişi ekleyinsrc_app
  4. göç etmek src_app; şema geçişinin en son (veri) geçişine, dest_appyani 3. adımın geçişine bağlı olduğundan emin olun

Tabloyu taşımak yerine tüm tabloyu kopyalayacağınızı unutmayın , ancak bu şekilde her iki uygulamanın da diğer uygulamaya ait bir masaya dokunması gerekmez, ki bence daha önemli.


0

TheModel modelini app_a'dan app_b'ye taşıdığınızı varsayalım.

Alternatif bir çözüm, mevcut geçişleri elle değiştirmektir. Buradaki fikir, app_a'nın geçişlerinde TheModel'i değiştiren bir işlem gördüğünüzde, bu işlemi app_b'nin ilk geçişinin sonuna kopyalamanızdır. Ve app_a'nın geçişlerinde bir 'app_a.TheModel' referansı gördüğünüzde, onu 'app_b.TheModel' olarak değiştirirsiniz.

Bunu, belirli bir modeli yeniden kullanılabilir bir uygulamaya çıkarmak istediğim mevcut bir proje için yaptım. Prosedür sorunsuz geçti. Sanırım app_b'den app_a'ya referanslar olsaydı işler çok daha zor olurdu. Ayrıca, modelim için yardımcı olabilecek manuel olarak tanımlanmış bir Meta.db_table vardı.

Özellikle değişmiş bir göç geçmişiyle karşılaşacaksınız. Orijinal geçişlerin uygulandığı bir veritabanınız olsa bile bu önemli değil. Hem orijinal hem de yeniden yazılan geçişler aynı veritabanı şemasıyla sonuçlanırsa, bu tür yeniden yazma işlemlerinin tamam olması gerekir.


0
  1. eski modellerin adlarını 'model_name_old' olarak değiştirin
  2. makemigrations
  3. İlgili modellerde aynı ilişkilere sahip 'model_name_new' adlı yeni modeller oluşturun (örneğin, kullanıcı modelinde artık user.blog_old ve user.blog_new var)
  4. makemigrations
  5. Tüm verileri yeni model tablolarına taşıyan özel bir geçiş yazın
  6. geçişleri çalıştırmadan önce ve çalıştırdıktan sonra yedekleri yeni veritabanı kopyalarıyla karşılaştırarak bu geçişleri test edin
  7. her şey tatmin edici olduğunda eski modelleri silin
  8. makemigrations
  9. yeni modelleri doğru adla değiştirin 'model_name_new' -> 'model_name'
  10. bir hazırlık sunucusunda tüm geçişleri test edin
  11. tüm taşıma işlemlerini kullanıcıların müdahalesi olmadan çalıştırmak için üretim sitenizi birkaç dakikalığına kapatın

Bunu, taşınması gereken her model için ayrı ayrı yapın. Tamsayılara ve yabancı anahtarlara geri dönerek diğer cevabın söylediğini yapmayı önermem Taşıma işlemlerinden sonra yeni yabancı anahtarların farklı olma ve satırların farklı kimliklere sahip olma ihtimali var ve herhangi bir risk almak istemedim yabancı anahtarlara geri dönerken eşleşmeyen kimliklerin.

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.