Je suis récemment passé de Django 1.6 à 1.7, et j'ai commencé à utiliser les migrations (je n'ai jamais utilisé South).
Avant la 1.7, j'avais l'habitude de charger les données initiales avec un fixture/initial_data.json
fichier, qui était chargé avec la python manage.py syncdb
commande (lors de la création de la base de données).
Maintenant, j'ai commencé à utiliser les migrations, et ce comportement est obsolète:
Si une application utilise des migrations, il n'y a pas de chargement automatique des appareils. Étant donné que des migrations seront nécessaires pour les applications dans Django 2.0, ce comportement est considéré comme obsolète. Si vous souhaitez charger les données initiales d'une application, envisagez de le faire dans une migration de données. ( https://docs.djangoproject.com/en/1.7/howto/initial-data/#automatically-loading-initial-data-fixtures )
La documentation officielle n'a pas d'exemple clair sur la façon de le faire, donc ma question est:
Quelle est la meilleure façon d'importer ces données initiales à l'aide de migrations de données:
- Écrire du code Python avec plusieurs appels à
mymodel.create(...)
, - Utilisez ou écrivez une fonction Django ( comme l'appel
loaddata
) pour charger des données à partir d'un fichier de fixture JSON.
Je préfère la deuxième option.
Je ne veux pas utiliser South, car Django semble être capable de le faire nativement maintenant.
Réponses:
Mise à jour : Voir le commentaire de @ GwynBleidD ci-dessous pour les problèmes que cette solution peut causer, et voir la réponse de @ Rockallite ci-dessous pour une approche plus durable pour les futurs changements de modèle.
En supposant que vous ayez un fichier de fixture dans
<yourapp>/fixtures/initial_data.json
Créez votre migration vide:
Dans Django 1.7:
Dans Django 1.8+, vous pouvez fournir un nom:
Modifier votre fichier de migration
<yourapp>/migrations/0002_auto_xxx.py
2.1. Implémentation personnalisée, inspirée de Django '
loaddata
(réponse initiale):2.2. Une solution plus simple pour
load_fixture
(selon la suggestion de @ juliocesar):Utile si vous souhaitez utiliser un répertoire personnalisé.
2.3. Simplest: appeler
loaddata
avecapp_label
accessoires volonté de charge du<yourapp>
« sfixtures
dir automatiquement:Si vous ne spécifiez pas
app_label
, loaddata essaiera de charger lefixture
nom de fichier à partir de tous les répertoires de fixtures des applications (ce que vous ne voulez probablement pas).Exécuter
la source
loaddata('loaddata', fixture_filename, app_label='<yourapp>')
ira également directement dans le répertoire de l'app fixture (donc pas besoin de construire le chemin complet du fixture)models.py
fichiers actuels , qui peuvent avoir des champs supplémentaires ou d'autres modifications. Si des modifications ont été apportées après la création de la migration, celle-ci échouera (nous ne pouvons donc même pas créer de migrations de schéma après cette migration). Pour résoudre ce problème, nous pouvons modifier le registre des applications sur lequel le sérialiseur travaille en registre fourni à la fonction de migration sur le premier paramètre. Le registre du chemin se trouve à l'adressedjango.core.serializers.python.apps
.app registry
, sans modifier une variable globale (ce qui pourrait causer des problèmes dans un avenir hypothétique avec des migrations de bases de données parallèles).Version courte
Vous ne devez PAS utiliser
loaddata
la commande de gestion directement dans une migration de données.Version longue
loaddata
utilisedjango.core.serializers.python.Deserializer
qui utilise les modèles les plus à jour pour désérialiser les données historiques dans une migration. C'est un comportement incorrect.Par exemple, supposons qu'il existe une migration de données qui utilise
loaddata
la commande de gestion pour charger les données d'un appareil, et qu'elle est déjà appliquée sur votre environnement de développement.Plus tard, vous décidez d'ajouter un nouveau champ obligatoire au modèle correspondant, donc vous le faites et effectuez une nouvelle migration par rapport à votre modèle mis à jour (et éventuellement fournissez une valeur unique au nouveau champ lorsque
./manage.py makemigrations
vous y êtes invité).Vous exécutez la prochaine migration et tout va bien.
Enfin, vous avez terminé de développer votre application Django et vous la déployez sur le serveur de production. Il est maintenant temps pour vous d'exécuter toutes les migrations à partir de zéro sur l'environnement de production.
Cependant, la migration des données échoue . En effet, le modèle désérialisé de la
loaddata
commande, qui représente le code actuel, ne peut pas être enregistré avec des données vides pour le nouveau champ obligatoire que vous avez ajouté. L'appareil d'origine ne dispose pas des données nécessaires pour cela!Mais même si vous mettez à jour l'appareil avec les données requises pour le nouveau champ, la migration des données échoue toujours . Lorsque la migration des données est en cours d'exécution, la prochaine migration qui ajoute la colonne correspondante à la base de données n'est pas encore appliquée. Vous ne pouvez pas enregistrer de données dans une colonne qui n'existe pas!
Conclusion: dans une migration de données, la
loaddata
commande introduit une éventuelle incohérence entre le modèle et la base de données. Vous ne devez certainement PAS l' utiliser directement dans une migration de données.La solution
loaddata
La commande s'appuie sur ladjango.core.serializers.python._get_model
fonction pour obtenir le modèle correspondant à partir d'un appareil, qui renverra la version la plus à jour d'un modèle. Nous devons le patcher de manière singulière pour qu'il obtienne le modèle historique.(Le code suivant fonctionne pour Django 1.8.x)
la source
objects = serializers.deserialize('json', fixture, ignorenonexistent=True)
souffrirait du même problème queloaddata
? Ouignorenonexistent=True
couvre-t-il tous les problèmes possibles?ignorenonexistent=True
argument a deux effets: 1) il ignore les modèles d'un appareil qui ne sont pas dans les définitions de modèle les plus courantes, 2) il ignore les champs d'un modèle d'un appareil qui ne le sont pas dans la définition de modèle correspondante la plus récente. Aucun d'entre eux ne gère la situation de nouveau champ obligatoire dans le modèle . Donc, oui, je pense qu'il souffre du même problème que la plaineloaddata
.natural_key()
, ce que cette méthode ne semble pas prendre en charge - je viens de remplacer la valeur natural_key par l'ID réel du modèle référencé.Inspiré par certains des commentaires (à savoir n__o) et le fait que j'ai beaucoup de
initial_data.*
fichiers répartis sur plusieurs applications, j'ai décidé de créer une application Django qui faciliterait la création de ces migrations de données.L' utilisation django-migration-appareil vous pouvez simplement exécuter la commande de gestion suivante et il recherche dans tous vos
INSTALLED_APPS
pour lesinitial_data.*
fichiers et les transformer en migrations de données.Voir django-migration-fixture pour les instructions d'installation / d'utilisation.
la source
Afin de donner à votre base de données des données initiales, écrivez une migration de données. Dans la migration de données, utilisez le RunPython fonction pour charger vos données.
N'écrivez aucune commande loaddata car cette méthode est obsolète.
Vos migrations de données ne seront exécutées qu'une seule fois. Les migrations sont une séquence ordonnée de migrations. Lorsque les migrations 003_xxxx.py sont exécutées, les migrations django écrit dans la base de données que cette application est migrée jusqu'à celle-ci (003) et exécutera uniquement les migrations suivantes.
la source
myModel.create(...)
(ou à utiliser une boucle) dans la fonction RunPython?Les solutions présentées ci-dessus n'ont malheureusement pas fonctionné pour moi. J'ai constaté que chaque fois que je change de modèle, je dois mettre à jour mes appareils. Idéalement, j'écrirais plutôt des migrations de données pour modifier les données créées et les données chargées par les appareils de la même manière.
Pour faciliter cela, j'ai écrit une fonction rapide qui cherchera dans le
fixtures
répertoire de l'application actuelle et chargera un appareil. Mettez cette fonction dans une migration au point de l'historique du modèle qui correspond aux champs de la migration.la source
RunPython(load_fixture('badger', 'stoat'))
. gist.github.com/danni/1b2a0078e998ac080111À mon avis, les luminaires sont un peu mauvais. Si votre base de données change fréquemment, les maintenir à jour deviendra bientôt un cauchemar. En fait, ce n'est pas seulement mon avis, dans le livre "Two Scoops of Django" c'est bien mieux expliqué.
Au lieu de cela, j'écrirai un fichier Python pour fournir la configuration initiale. Si vous avez besoin de quelque chose de plus, je vous suggère de regarder Factory boy .
Si vous avez besoin de migrer certaines données, vous devez utiliser des migrations de données .
Il y a aussi "Gravez vos appareils, utilisez des usines de modèles" sur l'utilisation des appareils.
la source
Sur Django 2.1, je voulais charger certains modèles (comme les noms de pays par exemple) avec les données initiales.
Mais je voulais que cela se produise automatiquement juste après l'exécution des migrations initiales.
J'ai donc pensé que ce serait formidable d'avoir un
sql/
dossier dans chaque application qui nécessitait le chargement des données initiales.Ensuite, dans ce
sql/
dossier, j'aurais des.sql
fichiers avec les DML requis pour charger les données initiales dans les modèles correspondants, par exemple:Pour être plus descriptif, voici à quoi
sql/
ressemblerait une application contenant un dossier:J'ai également trouvé des cas où j'avais besoin du
sql
scripts soient exécutés dans un ordre spécifique. J'ai donc décidé de préfixer les noms de fichiers avec un numéro consécutif comme le montre l'image ci-dessus.Ensuite, j'avais besoin d'un moyen de charger
SQLs
automatiquement tout disponible dans n'importe quel dossier d'application en faisantpython manage.py migrate
.J'ai donc créé une autre application nommée
initial_data_migrations
puis j'ai ajouté cette application à la liste desINSTALLED_APPS
dans lesettings.py
fichier. Ensuite, j'ai créé unmigrations
dossier à l'intérieur et ajouté un fichier appelérun_sql_scripts.py
( qui est en fait une migration personnalisée ). Comme le montre l'image ci-dessous:J'ai créé
run_sql_scripts.py
pour qu'il s'occupe d'exécuter tous lessql
scripts disponibles dans chaque application. Celui-ci est ensuite renvoyé lorsque quelqu'un courtpython manage.py migrate
. Cette coutumemigration
ajoute également les applications impliquées en tant que dépendances, de cette façon, elle tente d'exécuter lessql
instructions uniquement après que les applications requises ont exécuté leurs0001_initial.py
migrations (nous ne voulons pas tenter d'exécuter une instruction SQL sur une table inexistante).Voici la source de ce script:
J'espère que quelqu'un trouve cela utile, cela a très bien fonctionné pour moi !. Si vous avez des questions, n'hésitez pas à me le faire savoir.
REMARQUE: Ce n'est peut-être pas la meilleure solution puisque je ne fais que commencer avec django, mais je voulais toujours partager ce "Guide pratique" avec vous tous car je n'ai pas trouvé beaucoup d'informations en cherchant sur Google à ce sujet.
la source