Jak przenieść model między dwiema aplikacjami Django (Django 1.7)

134

Tak więc około rok temu zacząłem projekt i jak wszyscy nowi programiści nie skupiałem się zbytnio na strukturze, jednak teraz jestem dalej razem z Django zaczęło się okazywać, że układ mojego projektu głównie moje modele są okropne w strukturze .

Mam modele głównie przechowywane w jednej aplikacji i tak naprawdę większość z tych modeli powinna znajdować się w osobnych aplikacjach. Próbowałem rozwiązać ten problem i przenieść je na południe, ale uznałem to za trudne i naprawdę trudne z powodu obcych kluczy itp.

Jednak dzięki Django 1.7 i wbudowanej obsłudze migracji, czy istnieje lepszy sposób na zrobienie tego teraz?

Sam Buckingham
źródło
4
Możesz rozważyć zmianę zaakceptowanej odpowiedzi.
Babken Vardanyan
Dla osób, które spotkają się z tym w przyszłości: Django 3.x tutaj, a podejście opisane na realpython.com/move-django-model/ ... zadziałało dla mnie. Miałem wiele kluczy obcych między modelami w starej aplikacji i modelami w nowej aplikacji.
pradeepcep

Odpowiedzi:

19

Usuwam starą odpowiedź, ponieważ może to spowodować utratę danych. Jak wspomniano ozan , możemy utworzyć 2 migracje, po jednej w każdej aplikacji. Komentarze pod tym postem odnoszą się do mojej starej odpowiedzi.

Pierwsza migracja w celu usunięcia modelu z pierwszej aplikacji.

$ python manage.py makemigrations old_app --empty

Edytuj plik migracji, aby uwzględnić te operacje.

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)
    ]

Druga migracja, która zależy od pierwszej migracji i utwórz nową tabelę w drugiej aplikacji. Po przeniesieniu kodu modelu do drugiej aplikacji

$ python manage.py makemigrations new_app 

i edytuj plik migracji na coś takiego.

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)
    ]
ChillarAnand
źródło
Mam istniejące dane i dużo z nich, których po prostu nie mogę stracić, czy może to z tym zrobić?
Sam Buckingham
@KevinChristopherHenry Zmodyfikował kod. To zachowuje istniejące dane.
ChillarAnand
@SamBuckingham Tak, możesz spróbować przeprowadzić migrację ze zmodyfikowanym kodem bez utraty danych.
ChillarAnand
2
Myślę, że to będzie naprawdę najlepszy sposób, dziękuję za całą pomoc, to było genialne.
Sam Buckingham
1
IMO to złe rozwiązanie, podstawowym założeniem migracji jest to, że jeśli uruchomisz ./manage.py migratewszystko skończy się w dobrym stanie. Ręczne fałszowanie migracji to zły sposób IMO.
jb.
342

Można to zrobić dość łatwo za pomocą migrations.SeparateDatabaseAndState. Zasadniczo używamy operacji bazy danych do zmiany nazwy tabeli jednocześnie z dwiema operacjami stanu, aby usunąć model z historii jednej aplikacji i utworzyć go w historii innej.

Usuń ze starej aplikacji

python manage.py makemigrations old_app --empty

W migracji:

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)
    ]

Dodaj do nowej aplikacji

Najpierw skopiuj model do pliku model.py nowej aplikacji, a następnie:

python manage.py makemigrations new_app

Spowoduje to migrację z CreateModeloperacją naiwną jako jedyną operacją. Zawiń to w SeparateDatabaseAndStateoperacji, aby nie próbować odtwarzać tabeli. Uwzględnij również wcześniejszą migrację jako zależność:

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)
    ]
ozan
źródło
14
Naprawdę dobre wyjaśnienie. To powinna być odpowiedź, ponieważ zmiana nazwy tabeli pozwala uniknąć utraty jakichkolwiek danych.
Remiz
11
To najlepszy sposób na zrobienie tego i jest o wiele lepszy niż mój. Dodano uwagę u góry mojej odpowiedzi.
ChillarAnand
4
Zrobiłem to, ale kiedy po tym uruchomię "makemigrations" na nowej aplikacji, generuje migrację AlterModelTable zmieniając jej nazwę na None.
Diego Ponciano
4
Znalazłem sposób rozwiązania problemu na podstawie tych instrukcji. Problem jest bardziej skomplikowany, jeśli masz odniesienia do kluczy obcych, które są polami wymaganymi. Musiałem dodać kilka kroków, aby przenieść odniesienia.
Nostalg.io
14
Ze względu na wiele próśb stworzyłem szczegółową odpowiedź na temat migracji modelu FK na przykładzie GitHub. stackoverflow.com/questions/30601107/…
Nostalg.io
26

Napotkałem ten sam problem. Odpowiedź Ozana bardzo mi pomogła, ale niestety nie wystarczyła. Rzeczywiście, miałem kilka kluczy ForeignKey łączących się z modelem, który chciałem przenieść. Po pewnym bólu głowy znalazłem rozwiązanie, więc postanowiłem je opublikować, aby rozwiązać problem ludzi.

Potrzebujesz jeszcze 2 kroków:

  1. Zanim cokolwiek zrobisz, zmień wszystkie swoje ForeignKeylinki TheModelna Integerfield. Następnie uruchomićpython manage.py makemigrations
  2. Po wykonaniu czynności Ozana ponownie przekonwertuj swoje klucze obce: odłóż z powrotem ForeignKey(TheModel)zamiast IntegerField(). Następnie wykonaj migracje ponownie ( python manage.py makemigrations). Możesz następnie przeprowadzić migrację i powinno działać ( python manage.py migrate)

Mam nadzieję, że to pomoże. Oczywiście przetestuj go lokalnie, zanim spróbujesz w produkcji, aby uniknąć przykrych niespodzianek :)

otranzer
źródło
8
a co z relacjami ManyToManyField?
tomcounsell
1
@tomcounsell świetny komentarz, założyłbym, że dodając konkretny model przelotowy tylko na potrzeby migracji. Pozostawienie danych w stanie nienaruszonym wymaga wiele pracy ...
Wtower
Ponieważ relacja wiele do wielu to zwykle tylko tabela z dwoma kluczami obcymi, z punktu widzenia SQL można zastosować sztuczkę tej odpowiedzi. Ale żeby to osiągnąć tylko poprzez Django, jedno podejście, które przychodzi mi do głowy, byłoby podobne do @ozan answer, z wyjątkiem tego, że pierwszym krokiem byłoby zduplikowanie tabel związanych z relacją MTM (jedna wersja duplikatów w każdej aplikacji) , przeprowadź migrację wszystkich kluczy obcych do nowej aplikacji, a dopiero potem usuń duplikaty w starej aplikacji. Zastrzeżenie: nie testowałem :)
Arnaud P
15

Jak to zrobiłem (testowałem na Django == 1.8, z postgres, więc pewnie też 1.7)

Sytuacja

app1.YourModel

ale chcesz, aby trafił do: app2.YourModel

  1. Skopiuj YourModel (kod) z app1 do app2.
  2. dodaj to do app2.YourModel:

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

  4. Nowa migracja (np. 0009_auto_something.py) jest wykonywana w app2 z instrukcją migrations.CreateModel (), przenieś tę instrukcję do początkowej migracji app2 (np. 0001_initial.py) (będzie tak, jak zawsze tam była). A teraz usuń utworzoną migrację = 0009_auto_something.py

  5. Tak jak działasz, tak jak app2.YourModel zawsze tam był, teraz usuń istnienie app1.YourModel z migracji. Znaczenie: zakomentuj instrukcje CreateModel i każdą korektę lub migrację danych użytą później.

  6. I oczywiście każde odniesienie do app1.YourModel musi zostać zmienione w ramach projektu na app2.YourModel. Nie zapominaj również, że wszystkie możliwe klucze obce do app1.YourModel w migracjach muszą zostać zmienione na app2.

  7. Teraz, jeśli wykonasz $ python manage.py migrate, nic się nie zmieniło, również kiedy wykonujesz makemigracje $ python manage.py, nic nowego nie zostało wykryte.

  8. Teraz ostatnie poprawki: usuń metę klasy z app2.YourModel i wykonaj $ python manage.py makemigrations app2 && python manage.py migrate app2 (jeśli spojrzysz na tę migrację, zobaczysz coś takiego :)

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

table = None, oznacza, że ​​przyjmie domyślną nazwę tabeli, która w tym przypadku będzie app2_yourmodel.

  1. GOTOWE, z zapisanymi danymi.

PS podczas migracji zobaczy, że ten content_type app1.yourmodel został usunięty i można go usunąć. Możesz powiedzieć tak, ale tylko wtedy, gdy go nie używasz. Jeśli w dużym stopniu zależy Ci na tym, aby elementy FK do tego typu zawartości pozostały nienaruszone, nie odpowiadaj jeszcze tak lub nie, ale przejdź do bazy danych w tym czasie ręcznie i usuń typ zawartości app2.yourmodel i zmień nazwę typu zawartości app1. yourmodel do app2.yourmodel, a następnie kontynuuj, odpowiadając nie.

Michael van de Waeter
źródło
3
Chociaż to rozwiązanie jest zdecydowanie „trudniejsze” niż @ ozan i zdecydowanie wymaga większej edycji, to działało dobrze dla mnie (i można edytować migracje - zgodnie z dokumentacją mają być edytowalne).
pgcd,
1
Ewentualnie użyj również app_label = 'app1'opcji meta.
Wtower
Geniusz! To zadziałało świetnie w przypadku relacji ForeignKey. Przypuszczam, że zadziała to również w przypadku pól ManyToMany.
Babken Vardanyan
1
Podążałem za twoimi krokami, ale pole w pewnym modelu należącym do app1 składa się z klucza obcego z rekurencyjną relacją do modelu (myModel) do przeniesienia. Na przykład field1 = models.ForeignKey('app1.myModel').kiedy field1 was declared with a lazy reference to 'app1.myModel' but app 'app1' doesn't provide model 'MyModel'
migruję
12

Mam nerwowe migracje ręcznego kodowania (zgodnie z wymogami odpowiedzi Ozana ), więc poniższe łączy strategie Ozana i Michaela, aby zminimalizować ilość wymaganego ręcznego kodowania:

  1. Przed przeniesieniem jakichkolwiek modeli upewnij się, że pracujesz z czystą linią bazową, uruchamiając makemigrations .
  2. Przenieś kod modelu z app1doapp2
  3. Zgodnie z zaleceniami @Michael, wskazujemy nowy model na starą tabelę bazy danych, używając db_tableopcji Meta na „nowym” modelu:

    class Meta:
        db_table = 'app1_yourmodel'
    
  4. Biegnij makemigrations. Spowoduje to wygenerowanie CreateModelwapp2 i DeleteModelw app1. Technicznie rzecz biorąc, te migracje odnoszą się dokładnie do tej samej tabeli i spowodowałyby usunięcie (w tym wszystkie dane) i ponowne utworzenie tabeli.

  5. W rzeczywistości nie chcemy (ani nie musimy) nic robić przy stole. Potrzebujemy tylko Django, aby uwierzyć, że zmiana została dokonana. Zgodnie z odpowiedzią @ Ozana, state_operationsflaga w SeparateDatabaseAndStateto robi. Więc zawijamy wszystkie migrationswpisy W OBU PLIKACH MIGRACJI z SeparateDatabaseAndState(state_operations=[...]). Na przykład,

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

    staje się

    operations = [
        migrations.SeparateDatabaseAndState(state_operations=[
            ...
            migrations.DeleteModel(
                name='YourModel',
            ),
            ...
        ])
    ]
    
  6. Należy również upewnić się, że nowa CreateModelmigracja „wirtualna” zależy od migracji, która faktycznie utworzyła lub zmieniła oryginalną tabelę . Na przykład, jeśli Twoje nowe migracje są app2.migrations.0004_auto_<date>(dla Create) i app1.migrations.0007_auto_<date>(dla Delete), najprostszą rzeczą do zrobienia jest:

    • Otwórz app1.migrations.0007_auto_<date>i skopiuj jego app1zależność (np ('app1', '0006...'),.). Jest to migracja „bezpośrednio poprzedzająca” app1i powinna obejmować zależności od całej faktycznej logiki budowania modelu.
    • Otwórz app2.migrations.0004_auto_<date>i dodaj właśnie skopiowaną zależność do jej dependencieslisty.

Jeśli masz ForeignKeypowiązania z modelem, który przenosisz, powyższe może nie działać. Dzieje się tak, ponieważ:

  • Zależności nie są tworzone automatycznie dla ForeignKeyzmian
  • Nie chcemy opakowywać ForeignKeyzmian, state_operationswięc musimy upewnić się, że są one oddzielone od operacji na tabeli.

UWAGA: Django 2.2 dodało ostrzeżenie ( models.E028), które przerywa tę metodę. Możesz sobie z tym poradzić, managed=Falseale nie testowałem tego.

„Minimalny” zestaw operacji różni się w zależności od sytuacji, ale następująca procedura powinna działać w przypadku większości / wszystkich ForeignKeymigracji:

  1. SKOPIUJ model z app1na app2, ustaw db_table, ale NIE zmieniaj żadnych odniesień FK.
  2. Uruchom makemigrationsi zawiń całą app2migrację state_operations(patrz wyżej)
    • Jak wyżej, dodaj zależność app2 CreateTabledo ostatniej app1migracji
  3. Skieruj wszystkie odniesienia FK na nowy model. Jeśli nie używasz odniesień do ciągów, przenieś stary model na dół models.py(NIE usuwaj go), aby nie konkurował z importowaną klasą.
  4. Uruchom, makemigrationsale NIE zawijaj niczego state_operations(zmiany FK powinny faktycznie nastąpić). Dodaj zależność we wszystkich ForeignKeymigracjach (tj. AlterField) Do CreateTablemigracji w app2(będziesz potrzebować tej listy w następnym kroku, więc śledź je). Na przykład:

    • Znajdź migrację, która zawiera CreateModelnp. app2.migrations.0002_auto_<date>I skopiuj nazwę tej migracji.
    • Znajdź wszystkie migracje, które mają ForeignKey do tego modelu (np. Wyszukując app2.YourModelmigracje takie jak:

      class Migration(migrations.Migration):
      
          dependencies = [
              ('otherapp', '0001_initial'),
          ]
      
          operations = [
              migrations.AlterField(
                  model_name='relatedmodel',
                  name='fieldname',
                  field=models.ForeignKey(... to='app2.YourModel'),
              ),
          ]
      
    • Dodaj CreateModelmigrację jako zależność:

      class Migration(migrations.Migration):
      
          dependencies = [
              ('otherapp', '0001_initial'),
              ('app2', '0002_auto_<date>'),
          ]  
      
  5. Usuń modele z app1

  6. Uruchom makemigrationsi zawiń app1migrację state_operations.
    • Dodaj zależność do wszystkich ForeignKeymigracji (tj. AlterField) Z poprzedniego kroku (może obejmować migracje do app1i app2).
    • Kiedy utworzyłem te migracje, plik DeleteTable już zależało od AlterFieldmigracji, więc nie musiałem ich ręcznie wymuszać (tj. AlterWcześniej Delete).

W tym momencie Django jest gotowe. Nowy model wskazuje na starą tabelę, a migracje Django przekonały go, że wszystko zostało odpowiednio przeniesione. Dużym zastrzeżeniem (z odpowiedzi @ Michael) jest to, że ContentTypenowy model jest tworzony. Jeśli łączysz (np. Przez ForeignKey) z typami zawartości, musisz utworzyć migrację, aby zaktualizować ContentTypetabelę.

Chciałem wyczyścić po sobie (opcje Meta i nazwy tabel), więc zastosowałem następującą procedurę (od @Michael):

  1. Usuń db_tablewpis Meta
  2. Uruchom makemigrationsponownie, aby wygenerować zmianę nazwy bazy danych
  3. Edytuj tę ostatnią migrację i upewnij się, że zależy od DeleteTablemigracji. Wydaje się, że nie powinno to być konieczne, ponieważ Deletepowinno być czysto logiczne, ale napotykam błędy (np. app1_yourmodelNie istnieje), jeśli nie.
claytond
źródło
To zadziałało idealnie, dziękuję! Nie sądzę, aby edycja ostatniej migracji miała znaczenie, ponieważ i tak znajduje się ona na dole drzewa zależności.
James Meakin,
1
Dobra odpowiedź! Myślę, że do migracji trzeba dodać nawias zamykający. SeparateDatabaseAndState, prawda?
atm
To zadziałało dla mnie. Nie edytowałem też ostatniej migracji (krok 3, ostatnia linia całej odpowiedzi), jak @JamesMeakin i nadal działało dobrze
Megawatt
w drugim scenariuszu, tym z FK, drugi krok zawiódł mnie z błędem, który ma sens:table_name: (models.E028) db_table 'table_name' is used by multiple models: app1.Model, app2.Model.
Mihai Zamfir
Użyłem tej procedury kilka razy. Jeśli porównasz dokumentację dla 2.2 ( docs.djangoproject.com/en/2.2/ref/checks ) i 2.1 ( docs.djangoproject.com/en/2.1/ref/checks ), zobaczysz, że została dodana w 2.2. Może uda się to obejść, managed=Falseale nie mam gdzie to sprawdzić.
claytond
1

Inną hackerską alternatywą, jeśli dane nie są duże lub zbyt skomplikowane, ale nadal ważne do utrzymania, jest:

  • Pobierz osprzęt danych za pomocą manage.py dumpdata
  • Przejdź do modelowania zmian i migracji prawidłowo, bez powiązania zmian
  • Global zamień urządzenia ze starego modelu i nazw aplikacji na nowe
  • Załaduj dane za pomocą manage.py loaddata
Wtower
źródło
1

Skopiowano z mojej odpowiedzi na https://stackoverflow.com/a/47392970/8971048

W przypadku, gdy musisz przenieść model i nie masz już dostępu do aplikacji (lub nie chcesz dostępu), możesz utworzyć nową operację i rozważyć utworzenie nowego modelu tylko wtedy, gdy migrowany model nie ma istnieć.

W tym przykładzie przekazuję „MyModel” z old_app do myapp.

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))
            ],
        ),
    ]
Gal Singer
źródło
Nie dodawaj tej samej odpowiedzi do wielu pytań. Odpowiedz na najlepszą, a pozostałe oznacz jako duplikaty. Zobacz Czy można dodać zduplikowaną odpowiedź na kilka pytań?
Petter Friberg
0

Jest to testowane z grubsza, więc nie zapomnij wykonać kopii zapasowej bazy danych !!!

Na przykład istnieją dwie aplikacje: src_appi dst_app, chcemy przenieść model MoveMez src_appdo dst_app.

Utwórz puste migracje dla obu aplikacji:

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

Załóżmy, że nowe migracje to, XXX1_src_app_newa XXX1_dst_app_newpoprzednie najpopularniejsze migracje to XXX0_src_app_oldi XXX0_dst_app_old.

Dodaj operację, która zmienia nazwę tabeli dla MoveMemodelu i zmienia nazwę jej app_label w ProjectState na XXX1_dst_app_new. Nie zapomnij dodać zależności od XXX0_src_app_oldmigracji. Wynikowa XXX1_dst_app_newmigracja to:

# -*- 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'),
    ]

Dodaj zależność od XXX1_dst_app_newdo XXX1_src_app_new. XXX1_src_app_newto migracja bez operacji, która jest potrzebna, aby upewnić się, że przyszłe src_appmigracje zostaną wykonane później XXX1_dst_app_new.

Przejdź MoveMez src_app/models.pydo dst_app/models.py. Następnie uruchomić:

python manage.py migrate

To wszystko!

Sergey Fedoseev
źródło
Zauważ, że ten kod jest prawdopodobnie przydatny tylko dla django 1.7. Próba tego w django 2.0 nie zadziała. Oznacza to również, że używanie tego mechanizmu do przenoszenia modeli zwiększa koszty utrzymania aktualizacji wersji django.
Paul w 't Hout
0

Możesz spróbować następujących rzeczy (nieprzetestowane):

  1. przenieś model z src_appdodest_app
  2. migrować dest_app; upewnij się, że migracja schematu zależy od najnowszej src_appmigracji ( https://docs.djangoproject.com/en/dev/topics/migrations/#migration-files )
  3. dodać migrację danych do dest_app, która kopiuje wszystkie dane zsrc_app
  4. migrować src_app; upewnij się, że migracja schematu zależy od ostatniej migracji (danych) dest_app- to znaczy migracji z kroku 3

Pamiętaj, że będziesz kopiować zamiast przenosić całą tabelę, cały stół, ale w ten sposób obie aplikacje nie muszą dotykać tabeli należącej do drugiej aplikacji, co moim zdaniem jest ważniejsze.

Webthusiast
źródło
0

Powiedzmy, że przenosisz model TheModel z app_a do app_b.

Alternatywnym rozwiązaniem jest ręczna zmiana istniejących migracji. Chodzi o to, że za każdym razem, gdy widzisz operację zmieniającą TheModel w migracjach app_a, kopiujesz tę operację na koniec początkowej migracji app_b. Za każdym razem, gdy zobaczysz odniesienie „app_a.TheModel” w migracjach app_a, zmieniasz je na „app_b.TheModel”.

Zrobiłem to tylko dla istniejącego projektu, w którym chciałem wyodrębnić określony model do aplikacji wielokrotnego użytku. Procedura przebiegła sprawnie. Myślę, że byłoby znacznie trudniej, gdyby istniały odniesienia od app_b do app_a. Miałem również ręcznie zdefiniowaną tabelę Meta.db_table dla mojego modelu, co mogło pomóc.

Warto zauważyć, że skończysz ze zmienioną historią migracji. Nie ma to znaczenia, nawet jeśli masz bazę danych z zastosowanymi oryginalnymi migracjami. Jeśli zarówno migracje pierwotne, jak i przepisane, kończą się tym samym schematem bazy danych, takie przepisanie powinno być OK.

akaariai
źródło
0
  1. zmień nazwy starych modeli na „model_name_old”
  2. makemigracje
  3. tworzyć nowe modele o nazwie „nazwa_modelu_new” z identycznymi relacjami w powiązanych modelach (np. model użytkownika ma teraz user.blog_old i user.blog_new)
  4. makemigracje
  5. napisz migrację niestandardową, która migruje wszystkie dane do nowych tabel modelu
  6. przetestuj migracje do diabła, porównując kopie zapasowe z nowymi kopiami bazy danych przed i po przeprowadzeniu migracji
  7. kiedy wszystko jest w porządku, usuń stare modele
  8. makemigracje
  9. zmień nowe modele na poprawną nazwę „nazwa_modelu_new” -> „nazwa_modelu”
  10. przetestuj całą masę migracji na serwerze przejściowym
  11. Wyłącz witrynę produkcyjną na kilka minut, aby przeprowadzić wszystkie migracje bez ingerencji użytkowników

Zrób to indywidualnie dla każdego modelu, który ma zostać przeniesiony. Nie sugerowałbym robienia tego, co mówi druga odpowiedź, zmieniając na liczby całkowite iz powrotem na klucze obce Istnieje szansa, że ​​nowe klucze obce będą inne, a wiersze mogą mieć inne identyfikatory po migracji i nie chciałem ryzykować niezgodnych identyfikatorów podczas przełączania z powrotem na klucze obce.

tomcounsell
źródło