Vor ungefähr einem Jahr habe ich ein Projekt gestartet und wie alle neuen Entwickler habe ich mich nicht wirklich auf die Struktur konzentriert, aber jetzt, wo ich weiter mit Django zusammen bin, scheint es, dass mein Projektlayout, hauptsächlich meine Modelle, eine schreckliche Struktur haben .
Ich habe Modelle, die hauptsächlich in einer einzelnen App gespeichert sind, und die meisten dieser Modelle sollten sich in ihren eigenen Apps befinden. Ich habe versucht, dies zu beheben und sie nach Süden zu verschieben, fand es jedoch schwierig und aufgrund von Fremdschlüsseln sehr schwierig.
Gibt es aufgrund von Django 1.7 und der integrierten Unterstützung für Migrationen jetzt einen besseren Weg, dies zu tun?
Antworten:
Ich entferne die alte Antwort, da dies zu Datenverlust führen kann. Wie Ozan erwähnte , können wir in jeder App zwei Migrationen erstellen. Die Kommentare unter diesem Beitrag beziehen sich auf meine alte Antwort.
Erste Migration zum Entfernen des Modells aus der 1. App.
Bearbeiten Sie die Migrationsdatei, um diese Vorgänge einzuschließen.
Zweite Migration, die von der ersten Migration abhängt und die neue Tabelle in der 2. App erstellt. Nach dem Verschieben des Modellcodes in die 2. App
und bearbeiten Sie die Migrationsdatei in etwa so.
quelle
./manage.py migrate
alles in einem guten Zustand endet , wenn Sie es ausführen . Manuelles Fälschen von Migrationen ist IMO ein falscher Weg.Dies kann ziemlich einfach mit durchgeführt werden
migrations.SeparateDatabaseAndState
. Grundsätzlich verwenden wir eine Datenbankoperation, um die Tabelle gleichzeitig mit zwei Statusoperationen umzubenennen, um das Modell aus dem Verlauf einer App zu entfernen und in einer anderen zu erstellen.Aus alter App entfernen
In der Migration:
Zur neuen App hinzufügen
Kopieren Sie zuerst das Modell in die model.py der neuen App, dann:
Dies erzeugt eine Migration mit einer naiven
CreateModel
Operation als einziger Operation. Schließen Sie das in eineSeparateDatabaseAndState
Operation ein, damit wir nicht versuchen, die Tabelle neu zu erstellen. Schließen Sie auch die vorherige Migration als Abhängigkeit ein:quelle
Ich bin auf das gleiche Problem gestoßen. Ozans Antwort hat mir sehr geholfen, war aber leider nicht genug. In der Tat hatte ich mehrere ForeignKey-Links zu dem Modell, das ich verschieben wollte. Nach einigen Kopfschmerzen fand ich die Lösung und beschloss, sie zu veröffentlichen, um die Zeit der Leute zu lösen.
Sie benötigen 2 weitere Schritte:
ForeignKey
LinksTheModel
inIntegerfield
. Dann rennepython manage.py makemigrations
ForeignKey(TheModel)
stattIntegerField()
. Führen Sie dann die Migrationen erneut durch (python manage.py makemigrations
). Sie können dann migrieren und es sollte funktionieren (python manage.py migrate
)Ich hoffe es hilft. Testen Sie es natürlich vor Ort, bevor Sie versuchen, schlechte Überraschungen zu vermeiden :)
quelle
Wie ich es gemacht habe (getestet auf Django == 1.8, mit Postgres, also wahrscheinlich auch 1.7)
Lage
app1.IhrModell
aber Sie möchten, dass es zu: app2.YourModel geht
füge dies zu app2 hinzu. DeinModell:
$ python manage.py makemigrations app2
In app2 wird eine neue Migration (z. B. 0009_auto_something.py) mit einer Anweisung migrations.CreateModel () durchgeführt. Verschieben Sie diese Anweisung in die anfängliche Migration von app2 (z. B. 0001_initial.py) (es wird so sein, wie es immer dort war). Und jetzt entfernen Sie die erstellte Migration = 0009_auto_something.py
So wie Sie sich verhalten, wie es app2.IhrModel schon immer war, entfernen Sie jetzt die Existenz von app1.IhrModell aus Ihren Migrationen. Bedeutung: Kommentieren Sie die CreateModel-Anweisungen und jede Anpassung oder Datenmigration aus, die Sie danach verwendet haben.
Und natürlich muss jeder Verweis auf app1.YourModel durch Ihr Projekt in app2.YourModel geändert werden. Vergessen Sie auch nicht, dass alle möglichen Fremdschlüssel für app1.YourModel in Migrationen in app2.YourModel geändert werden müssen
Wenn Sie nun $ python manage.py migrieren, hat sich nichts geändert, auch wenn Sie $ python manage.py makemigrations durchführen, wurde nichts Neues erkannt.
Jetzt der letzte Schliff: Entfernen Sie die Klassen-Meta aus app2.IhrModell und führen Sie $ python manage.py makemigrations app2 && python manage.py migrate app2 aus (wenn Sie sich diese Migration ansehen, sehen Sie so etwas :)
table = None bedeutet, dass der Standardtabellenname verwendet wird, in diesem Fall app2_yourmodel.
PS Während der Migration wird angezeigt, dass der Inhaltstyp app1.yourmodel entfernt wurde und gelöscht werden kann. Sie können dem zustimmen, aber nur, wenn Sie es nicht verwenden. Wenn Sie stark davon abhängig sind, dass FKs für diesen Inhaltstyp intakt sind, antworten Sie noch nicht mit Ja oder Nein, sondern gehen Sie zu diesem Zeitpunkt manuell in die Datenbank, entfernen Sie den Inhaltstyp app2.Ihr Modell und benennen Sie den Inhaltstyp app1 um. yourmodel to app2.yourmodel, und fahren Sie dann fort, indem Sie mit Nein antworten.
quelle
app_label = 'app1'
Meta-Option.field1 = models.ForeignKey('app1.myModel').
bei der Migration erhalte ich einen ValueError, der besagt, dassfield1 was declared with a lazy reference to 'app1.myModel' but app 'app1' doesn't provide model 'MyModel'
Ich bekomme nervöse Handcodierungsmigrationen (wie in Ozans Antwort gefordert ), daher werden im Folgenden die Strategien von Ozan und Michael kombiniert , um den Umfang der erforderlichen Handcodierung zu minimieren:
makemigrations
.app1
nachapp2
Wie von @Michael empfohlen, verweisen wir das neue Modell mit der
db_table
Option Meta auf das "neue" Modell auf die alte Datenbanktabelle :Ausführen
makemigrations
. Dies wirdCreateModel
inapp2
undDeleteModel
in generierenapp1
. Technisch gesehen beziehen sich diese Migrationen auf genau dieselbe Tabelle und würden die Tabelle entfernen (einschließlich aller Daten) und neu erstellen.In Wirklichkeit wollen (oder müssen) wir nichts mit dem Tisch anfangen. Wir brauchen nur Django, um zu glauben, dass die Änderung vorgenommen wurde. Per @ Ozans Antwort macht die
state_operations
Flagge inSeparateDatabaseAndState
dies. Also verpacken wir allemigrations
Einträge in BEIDE MIGRATIONSDATEIEN mitSeparateDatabaseAndState(state_operations=[...])
. Beispielsweise,wird
Sie müssen auch sicherstellen, dass die neue "virtuelle"
CreateModel
Migration von jeder Migration abhängt, die die ursprüngliche Tabelle tatsächlich erstellt oder geändert hat . Wenn Ihre neuen Migrationen beispielsweiseapp2.migrations.0004_auto_<date>
(fürCreate
) undapp1.migrations.0007_auto_<date>
(fürDelete
) sind, ist dies am einfachsten:app1.migrations.0007_auto_<date>
und kopieren Sie dieapp1
Abhängigkeit (z('app1', '0006...'),
. B. ). Dies ist die "unmittelbar vorherige" Migration inapp1
und sollte Abhängigkeiten von der gesamten tatsächlichen Modellbildungslogik enthalten.app2.migrations.0004_auto_<date>
Sie die soeben kopierte Abhängigkeit und fügen Sie sie in diedependencies
Liste ein.Wenn Sie eine
ForeignKey
Beziehung zu dem Modell haben, das Sie verschieben, funktioniert dies möglicherweise nicht. Dies geschieht, weil:ForeignKey
Änderungen nicht automatisch erstelltForeignKey
Änderungen nicht einschließen,state_operations
daher müssen wir sicherstellen, dass sie von den Tabellenoperationen getrennt sind.HINWEIS: Django 2.2 hat eine Warnung (
models.E028
) hinzugefügt , die diese Methode unterbricht. Sie können es vielleicht umgehen,managed=False
aber ich habe es nicht getestet.Die "minimalen" Vorgänge unterscheiden sich je nach Situation, aber das folgende Verfahren sollte für die meisten / alle
ForeignKey
Migrationen funktionieren :app1
bisapp2
, setzen Siedb_table
, aber ändern Sie KEINE FK-Referenzen.makemigrations
gesamteapp2
Migration aus und schließen Sie sie einstate_operations
(siehe oben).app2
CreateTable
der neuestenapp1
Migration hinzumodels.py
(entfernen Sie es NICHT), damit es nicht mit der importierten Klasse konkurriert.Führen Sie aus,
makemigrations
aber wickeln Sie nichts einstate_operations
(die FK-Änderungen sollten tatsächlich stattfinden). Fügen Sie eine Abhängigkeit in allenForeignKey
Migrationen (dhAlterField
) zurCreateTable
Migration in hinzuapp2
(Sie benötigen diese Liste für den nächsten Schritt, behalten Sie sie im Auge). Beispielsweise:CreateModel
eg enthält,app2.migrations.0002_auto_<date>
und kopieren Sie den Namen dieser Migration.Finden Sie alle Migrationen, die einen ForeignKey für dieses Modell haben (z. B. indem Sie nach
app2.YourModel
Migrationen suchen wie:Fügen Sie die
CreateModel
Migration als Abhängigkeit hinzu:Entfernen Sie die Modelle aus
app1
makemigrations
dieapp1
Migration aus und schließen Sie sie einstate_operations
.ForeignKey
Migrationen (dhAlterField
) aus dem vorherigen Schritt eine Abhängigkeit hinzu (einschließlich Migrationen inapp1
undapp2
).DeleteTable
hing die bereits von denAlterField
Migrationen ab, sodass ich sie nicht manuell erzwingen musste (dhAlter
vorherDelete
).An diesem Punkt ist Django gut zu gehen. Das neue Modell verweist auf die alte Tabelle und Djangos Migrationen haben ihn davon überzeugt, dass alles angemessen verschoben wurde. Die große Einschränkung (aus @ Michaels Antwort) ist, dass
ContentType
für das neue Modell ein neues erstellt wird. Wenn SieForeignKey
mit Inhaltstypen verknüpfen (z. B. nach ), müssen Sie eine Migration erstellen, um dieContentType
Tabelle zu aktualisieren .Ich wollte nach mir selbst aufräumen (Meta-Optionen und Tabellennamen), also habe ich das folgende Verfahren angewendet (von @Michael):
db_table
Meta-Eintragmakemigrations
erneut aus, um die Datenbankumbenennung zu generierenDeleteTable
Migration abhängt . Es scheint nicht notwendig zu sein, daDelete
dies rein logisch sein sollte, aber ich bin auf Fehler gestoßen (zBapp1_yourmodel
existiert nicht), wenn ich es nicht tue.quelle
table_name: (models.E028) db_table 'table_name' is used by multiple models: app1.Model, app2.Model.
managed=False
aber ich bin nicht in der Lage, dies zu überprüfen.Eine andere hackige Alternative, wenn die Daten nicht groß oder zu kompliziert sind, aber dennoch wichtig zu pflegen sind, ist:
quelle
Von meiner Antwort unter https://stackoverflow.com/a/47392970/8971048 kopiert
Wenn Sie das Modell verschieben müssen und keinen Zugriff mehr auf die App haben (oder keinen Zugriff mehr möchten), können Sie eine neue Operation erstellen und in Betracht ziehen, nur dann ein neues Modell zu erstellen, wenn das migrierte Modell dies nicht tut existieren.
In diesem Beispiel übergebe ich 'MyModel' von old_app an myapp.
quelle
Dies wird grob getestet, vergessen Sie also nicht, Ihre Datenbank zu sichern !!!
Zum Beispiel gibt es zwei Apps:
src_app
unddst_app
wir möchten das ModellMoveMe
vonsrc_app
nach verschiebendst_app
.Erstellen Sie leere Migrationen für beide Apps:
Nehmen wir an, dass neue Migrationen
XXX1_src_app_new
undXXX1_dst_app_new
, die vorherigen Top-MigrationenXXX0_src_app_old
und sindXXX0_dst_app_old
.Fügen Sie eine Operation hinzu, die die Tabelle für das
MoveMe
Modell umbenennt und das app_label in ProjectState in umbenenntXXX1_dst_app_new
. Vergessen Sie nicht, die Abhängigkeit von derXXX0_src_app_old
Migration hinzuzufügen . Die resultierendeXXX1_dst_app_new
Migration ist:Fügen Sie die Abhängigkeit
XXX1_dst_app_new
zu hinzuXXX1_src_app_new
.XXX1_src_app_new
ist eine No-Op-Migration, die erforderlich ist, um sicherzustellen, dass zukünftigesrc_app
Migrationen danach ausgeführt werdenXXX1_dst_app_new
.Bewegen Sie sich
MoveMe
vonsrc_app/models.py
nachdst_app/models.py
. Dann renne:Das ist alles!
quelle
Sie können Folgendes versuchen (ungetestet):
src_app
nachdest_app
dest_app
; Stellen Sie sicher, dass die Schemamigration von der neuestensrc_app
Migration abhängt ( https://docs.djangoproject.com/de/dev/topics/migrations/#migration-files ).dest_app
, von der alle Daten kopiert werdensrc_app
src_app
; Stellen Sie sicher, dass die Schemamigration von der letzten (Daten-) Migration von abhängtdest_app
- dh von der Migration von Schritt 3Beachten Sie, dass Sie sein werden die gesamte Tabelle kopieren , anstatt sie zu verschieben. Auf diese Weise müssen beide Apps jedoch keine Tabelle berühren, die zur anderen App gehört, was meiner Meinung nach wichtiger ist.
quelle
Nehmen wir an, Sie verschieben das Modell TheModel von app_a nach app_b.
Eine alternative Lösung besteht darin, die vorhandenen Migrationen von Hand zu ändern. Die Idee ist, dass Sie jedes Mal, wenn Sie eine Operation sehen, die TheModel in den Migrationen von app_a ändert, diese Operation bis zum Ende der anfänglichen Migration von app_b kopieren. Und jedes Mal, wenn Sie in den Migrationen von app_a eine Referenz 'app_a.TheModel' sehen, ändern Sie diese in 'app_b.TheModel'.
Ich habe dies gerade für ein vorhandenes Projekt getan, bei dem ich ein bestimmtes Modell in eine wiederverwendbare App extrahieren wollte. Das Verfahren verlief reibungslos. Ich denke, es wäre viel schwieriger, wenn es Verweise von app_b auf app_a gäbe. Außerdem hatte ich eine manuell definierte Meta.db_table für mein Modell, die möglicherweise geholfen hat.
Insbesondere wird der Migrationsverlauf geändert. Dies spielt keine Rolle, selbst wenn Sie eine Datenbank mit den ursprünglich angewendeten Migrationen haben. Wenn sowohl die ursprüngliche als auch die umgeschriebene Migration dasselbe Datenbankschema haben, sollte eine solche Umschreibung in Ordnung sein.
quelle
Tun Sie dies individuell für jedes Modell, das verschoben werden muss. Ich würde nicht vorschlagen, das zu tun, was in der anderen Antwort steht, indem Sie zu Ganzzahlen und zurück zu Fremdschlüsseln wechseln. Es besteht die Möglichkeit, dass neue Fremdschlüssel unterschiedlich sind und Zeilen nach den Migrationen möglicherweise andere IDs haben, und ich wollte kein Risiko eingehen von nicht übereinstimmenden IDs beim Zurückschalten auf Fremdschlüssel.
quelle