Ich habe kürzlich von Django 1.6 auf 1.7 umgestellt und angefangen, Migrationen zu verwenden (ich habe South nie verwendet).
Vor 1.7 habe ich Anfangsdaten mit einer fixture/initial_data.json
Datei geladen, die mit dem python manage.py syncdb
Befehl geladen wurde (beim Erstellen der Datenbank).
Jetzt habe ich angefangen, Migrationen zu verwenden, und dieses Verhalten ist veraltet:
Wenn eine Anwendung Migrationen verwendet, werden keine Fixtures automatisch geladen. Da für Anwendungen in Django 2.0 Migrationen erforderlich sind, gilt dieses Verhalten als veraltet. Wenn Sie Anfangsdaten für eine App laden möchten, sollten Sie dies bei einer Datenmigration in Betracht ziehen. ( https://docs.djangoproject.com/de/1.7/howto/initial-data/#automatically-loading-initial-data-fixtures )
Die offizielle Dokumentation enthält kein klares Beispiel dafür, daher lautet meine Frage:
Was ist der beste Weg, um solche Anfangsdaten mithilfe von Datenmigrationen zu importieren:
- Schreiben Sie Python-Code mit mehreren Aufrufen an
mymodel.create(...)
, - Verwenden oder schreiben Sie eine Django-Funktion ( wie das Aufrufen
loaddata
), um Daten aus einer JSON-Fixture-Datei zu laden.
Ich bevorzuge die zweite Option.
Ich möchte South nicht verwenden, da Django dies jetzt nativ zu tun scheint.
Antworten:
Update : Siehe @ GwynBleidDs Kommentar unten für die Probleme, die diese Lösung verursachen kann, und siehe @ Rockallites Antwort unten für einen Ansatz, der für zukünftige Modelländerungen dauerhafter ist.
Angenommen, Sie haben eine Fixture-Datei in
<yourapp>/fixtures/initial_data.json
Erstellen Sie Ihre leere Migration:
In Django 1.7:
In Django 1.8+ können Sie einen Namen angeben:
Bearbeiten Sie Ihre Migrationsdatei
<yourapp>/migrations/0002_auto_xxx.py
2.1. Benutzerdefinierte Implementierung, inspiriert von Django '
loaddata
(erste Antwort):2.2. Eine einfachere Lösung für
load_fixture
(per @ juliocesar Vorschlag):Nützlich, wenn Sie ein benutzerdefiniertes Verzeichnis verwenden möchten.
2.3. Einfachstes: Aufruf
loaddata
mitapp_label
Willen Lastbefestigungen aus dem<yourapp>
‚sfixtures
dir automatisch:Wenn Sie nichts angeben
app_label
, versucht loaddata, denfixture
Dateinamen aus allen Apps-Fixtures-Verzeichnissen zu laden (was Sie wahrscheinlich nicht möchten).Starte es
quelle
loaddata('loaddata', fixture_filename, app_label='<yourapp>')
geht direkt zum App-Fixture-Verzeichnis (daher muss nicht der vollständige Pfad desmodels.py
Dateien, die einige zusätzliche Felder oder andere Änderungen enthalten können. Wenn nach dem Erstellen der Migration einige Änderungen vorgenommen wurden, schlägt dies fehl (daher können wir nach dieser Migration nicht einmal Schemamigrationen erstellen). Um dies zu beheben, können wir die Apps-Registrierung, an der der Serializer arbeitet, teporaly in die Registrierung ändern, die für die Migrationsfunktion beim ersten Parameter bereitgestellt wird. Die Registrierung zum Pfad befindet sich unterdjango.core.serializers.python.apps
.app registry
, ohne eine globale Variable zu ändern (was in einer hypothetischen Zukunft bei parallelen Datenbankmigrationen zu Problemen führen kann) ?Kurzfassung
Sie sollten den Verwaltungsbefehl NICHT
loaddata
direkt in einer Datenmigration verwenden.Lange Version
loaddata
nutzt ,django.core.serializers.python.Deserializer
die die meisten up-to-date verwendet Modelle , historische Daten in einer Migration deserialisieren. Das ist falsches Verhalten.Angenommen, es gibt eine Datenmigration, bei der der
loaddata
Verwaltungsbefehl zum Laden von Daten von einem Fixture verwendet wird und die bereits auf Ihre Entwicklungsumgebung angewendet wird.Später beschließen Sie, dem entsprechenden Modell ein neues erforderliches Feld hinzuzufügen. Führen Sie dies aus und führen Sie eine neue Migration für Ihr aktualisiertes Modell durch (und geben Sie dem neuen Feld möglicherweise einen einmaligen Wert, wenn
./manage.py makemigrations
Sie dazu aufgefordert werden ).Sie führen die nächste Migration aus und alles ist gut.
Schließlich sind Sie mit der Entwicklung Ihrer Django-Anwendung fertig und stellen sie auf dem Produktionsserver bereit. Jetzt ist es Zeit für Sie, die gesamten Migrationen in der Produktionsumgebung von Grund auf neu auszuführen.
Die Datenmigration schlägt jedoch fehl . Dies liegt daran, dass das deserialisierte Modell aus dem
loaddata
Befehl, der den aktuellen Code darstellt, nicht mit leeren Daten für das neue erforderliche Feld gespeichert werden kann, das Sie hinzugefügt haben. Dem Originalgerät fehlen die notwendigen Daten dafür!Aber selbst wenn Sie das Gerät mit den erforderlichen Daten für das neue Feld aktualisieren, schlägt die Datenmigration immer noch fehl . Wenn die Datenmigration ausgeführt wird, wird die nächste Migration, bei der die entsprechende Spalte zur Datenbank hinzugefügt wird, noch nicht angewendet. Sie können keine Daten in einer nicht vorhandenen Spalte speichern!
Schlussfolgerung: Bei einer Datenmigration führt der
loaddata
Befehl zu potenziellen Inkonsistenzen zwischen dem Modell und der Datenbank. Sie sollten es definitiv NICHT direkt in einer Datenmigration verwenden.Die Lösung
loaddata
Der Befehl basiert auf derdjango.core.serializers.python._get_model
Funktion, um das entsprechende Modell von einem Gerät abzurufen, das die aktuellste Version eines Modells zurückgibt. Wir müssen es mit Affen patchen, damit es das historische Modell erhält.(Der folgende Code funktioniert für Django 1.8.x)
quelle
objects = serializers.deserialize('json', fixture, ignorenonexistent=True)
unter demselben Problem leiden würde wieloaddata
? Oderignorenonexistent=True
deckt alle möglichen Probleme ab?ignorenonexistent=True
Argument zwei Auswirkungen hat: 1) Es ignoriert Modelle eines Geräts, die nicht in den aktuellsten Modelldefinitionen enthalten sind, 2) Es ignoriert Felder eines Modells eines Geräts, die dies nicht sind in der aktuellsten entsprechenden Modelldefinition. Keiner von ihnen behandelt die Situation des neuen erforderlichen Feldes im Modell . Also, ja, ich denke, es hat das gleiche Problem wie einfachloaddata
.natural_key()
, was diese Methode nicht zu unterstützen scheint. Ich habe nur den Wert natural_key durch die tatsächliche ID des referenzierten Modells ersetzt.Inspiriert von einigen Kommentaren (n__o's) und der Tatsache, dass viele
initial_data.*
Dateien auf mehrere Apps verteilt sind, habe ich beschlossen, eine Django-App zu erstellen, die die Erstellung dieser Datenmigrationen erleichtert.Mit django-Migration-Halterung können Sie einfach den folgenden Management - Befehl ausführen , und es wird durch all Ihre Suche
INSTALLED_APPS
nachinitial_data.*
Dateien und sie in Datenmigrationen.Anweisungen zur Installation / Verwendung finden Sie unter django-migrations-fixture .
quelle
Schreiben Sie eine Datenmigration , um Ihrer Datenbank erste Daten zu geben . Verwenden Sie bei der Datenmigration die RunPython- Funktion, um Ihre Daten zu laden.
Schreiben Sie keinen Befehl loaddata, da dieser Weg veraltet ist.
Ihre Datenmigrationen werden nur einmal ausgeführt. Die Migrationen sind eine geordnete Folge von Migrationen. Wenn die Migrationen 003_xxxx.py ausgeführt werden, schreibt django migrations in die Datenbank, dass diese App bis zu dieser migriert wird (003), und führt nur die folgenden Migrationen aus.
quelle
myModel.create(...)
(oder die Verwendung einer Schleife) in der RunPython-Funktion zu wiederholen ?Die oben vorgestellten Lösungen haben bei mir leider nicht funktioniert. Ich habe festgestellt, dass ich jedes Mal, wenn ich meine Modelle ändere, meine Geräte aktualisieren muss. Im Idealfall würde ich stattdessen Datenmigrationen schreiben, um erstellte Daten und mit Geräten geladene Daten auf ähnliche Weise zu ändern.
Um dies zu erleichtern, habe ich eine Schnellfunktion geschrieben, die im
fixtures
Verzeichnis der aktuellen App nachschaut und ein Gerät lädt. Fügen Sie diese Funktion an der Stelle des Modellverlaufs in eine Migration ein, die mit den Feldern in der Migration übereinstimmt.quelle
RunPython(load_fixture('badger', 'stoat'))
. gist.github.com/danni/1b2a0078e998ac080111Meiner Meinung nach sind die Spiele etwas schlecht. Wenn sich Ihre Datenbank häufig ändert, wird es bald ein Albtraum sein, sie auf dem neuesten Stand zu halten. Eigentlich ist es nicht nur meine Meinung, in dem Buch "Two Scoops of Django" wird es viel besser erklärt.
Stattdessen schreibe ich eine Python-Datei, um die Ersteinrichtung zu ermöglichen. Wenn Sie etwas mehr brauchen, schlage ich vor, dass Sie sich Factory Boy ansehen .
Wenn Sie einige Daten migrieren müssen, sollten Sie Datenmigrationen verwenden .
Es gibt auch "Brennen Sie Ihre Geräte, verwenden Sie Modellfabriken" über die Verwendung von Geräten.
quelle
Auf Django 2.1 wollte ich einige Modelle (wie z. B. Ländernamen) mit Anfangsdaten laden.
Ich wollte jedoch, dass dies direkt nach der Ausführung der ersten Migrationen automatisch geschieht.
Daher dachte ich, dass es großartig wäre,
sql/
in jeder Anwendung einen Ordner zu haben , in den die anfänglichen Daten geladen werden müssen.Dann
sql/
hätte ich in diesem Ordner.sql
Dateien mit den erforderlichen DMLs, um die Anfangsdaten in die entsprechenden Modelle zu laden, zum Beispiel:Um es genauer zu beschreiben,
sql/
würde eine App mit einem Ordner folgendermaßen aussehen:Außerdem habe ich einige Fälle gefunden, in denen die
sql
Skripte in einer bestimmten Reihenfolge ausgeführt werden mussten. Deshalb habe ich beschlossen, den Dateinamen eine fortlaufende Nummer voranzustellen, wie im obigen Bild gezeigt.Dann brauchte ich eine Möglichkeit, alle
SQLs
verfügbaren Inhalte in einem Anwendungsordner automatisch zu ladenpython manage.py migrate
.Also habe ich eine andere Anwendung mit dem Namen
initial_data_migrations
und dann habe ich diese App in die Liste derINSTALLED_APPS
insettings.py
Datei. Dann habe ich einenmigrations
Ordner darin erstellt und eine Datei namensrun_sql_scripts.py
( was eigentlich eine benutzerdefinierte Migration ist ) hinzugefügt . Wie im Bild unten zu sehen:Ich habe es
run_sql_scripts.py
so erstellt , dass allesql
in jeder Anwendung verfügbaren Skripte ausgeführt werden. Dieser wird dann abgefeuert, wenn jemand renntpython manage.py migrate
. Dieser Brauchmigration
fügt auch die beteiligten Anwendungen als Abhängigkeiten hinzu. Auf diese Weise wird versucht, diesql
Anweisungen erst auszuführen, nachdem die erforderlichen Anwendungen ihre0001_initial.py
Migrationen ausgeführt haben (wir möchten nicht versuchen, eine SQL-Anweisung für eine nicht vorhandene Tabelle auszuführen ).Hier ist die Quelle dieses Skripts:
Ich hoffe, jemand findet das hilfreich, es hat gut für mich funktioniert!. Wenn Sie Fragen haben, lassen Sie es mich bitte wissen.
HINWEIS: Dies ist möglicherweise nicht die beste Lösung, da ich gerade erst mit Django angefangen habe, aber trotzdem dieses "How-to" mit Ihnen allen teilen wollte, da ich beim Googeln nicht viele Informationen gefunden habe.
quelle