Donc, il y a environ un an, j'ai commencé un projet et comme tous les nouveaux développeurs, je ne me suis pas vraiment concentré sur la structure, mais maintenant je suis plus avancé avec Django, il a commencé à apparaître que la mise en page de mon projet, principalement mes modèles sont horribles dans la structure .
J'ai des modèles principalement détenus dans une seule application et vraiment la plupart de ces modèles devraient être dans leurs propres applications individuelles, j'ai essayé de résoudre cela et de les déplacer avec le sud, mais je l'ai trouvé délicat et vraiment difficile en raison de clés étrangères, etc.
Cependant, en raison de Django 1.7 et de la prise en charge intégrée des migrations, y a-t-il un meilleur moyen de le faire maintenant?
Réponses:
Je supprime l'ancienne réponse car cela peut entraîner une perte de données. Comme ozan l'a mentionné , nous pouvons créer 2 migrations une dans chaque application. Les commentaires sous cet article font référence à mon ancienne réponse.
Première migration pour supprimer le modèle de la 1ère application.
Modifiez le fichier de migration pour inclure ces opérations.
Deuxième migration qui dépend de la première migration et créer la nouvelle table dans la deuxième application. Après avoir déplacé le code du modèle vers la 2ème application
et modifiez le fichier de migration en quelque chose comme ça.
la source
./manage.py migrate
tout se terminera en bon état. Simuler manuellement les migrations est une mauvaise manière pour l'OMI.Cela peut être fait assez facilement en utilisant
migrations.SeparateDatabaseAndState
. Fondamentalement, nous utilisons une opération de base de données pour renommer la table simultanément avec deux opérations d'état pour supprimer le modèle de l'historique d'une application et le créer dans celui d'une autre.Supprimer de l'ancienne application
Dans la migration:
Ajouter à une nouvelle application
Tout d'abord, copiez le modèle dans le model.py de la nouvelle application, puis:
Cela générera une migration avec une
CreateModel
opération naïve comme seule opération. Enveloppez cela dans uneSeparateDatabaseAndState
opération telle que nous n'essayons pas de recréer la table. Incluez également la migration précédente en tant que dépendance:la source
J'ai rencontré le même problème. La réponse d'Ozan m'a beaucoup aidé mais malheureusement pas assez. En effet j'avais plusieurs ForeignKey liés au modèle que je souhaitais déplacer. Après quelques maux de tête, j'ai trouvé la solution alors j'ai décidé de la poster pour résoudre le temps des gens.
Vous avez besoin de 2 étapes supplémentaires:
ForeignKey
liensTheModel
enIntegerfield
. Puis courspython manage.py makemigrations
ForeignKey(TheModel)
lieu deIntegerField()
. Puis refaites les migrations (python manage.py makemigrations
). Vous pouvez ensuite migrer et cela devrait fonctionner (python manage.py migrate
)J'espère que ça aide. Bien sûr, testez-le en local avant d'essayer en production pour éviter les mauvaises surprises :)
la source
Comment je l'ai fait (testé sur Django == 1.8, avec postgres, donc probablement aussi 1.7)
Situation
app1.YourModel
mais vous voulez qu'il aille sur: app2.YourModel
ajoutez ceci à app2.YourModel:
$ python manage.py makemigrations app2
Une nouvelle migration (par exemple 0009_auto_something.py) est effectuée dans app2 avec une instruction migrations.CreateModel (), déplacez cette instruction vers la migration initiale de app2 (par exemple 0001_initial.py) (ce sera comme si elle avait toujours été là). Et maintenant, supprimez la migration créée = 0009_auto_something.py
Tout comme vous agissez, comme app2.YourModel a toujours été là, supprimez maintenant l'existence de app1.YourModel de vos migrations. Signification: commentez les instructions CreateModel et chaque ajustement ou migration de données que vous avez utilisé par la suite.
Et bien sûr, chaque référence à app1.YourModel doit être remplacée par app2.YourModel via votre projet. N'oubliez pas non plus que toutes les clés étrangères possibles vers app1.YourModel dans les migrations doivent être remplacées par app2.YourModel
Maintenant, si vous faites migrer $ python manage.py, rien n'a changé, même lorsque vous faites $ python manage.py makemigrations, rien de nouveau n'a été détecté.
Maintenant, la touche finale: supprimez la classe Meta de app2.YourModel et faites $ python manage.py makemigrations app2 && python manage.py migrez app2 (si vous examinez cette migration, vous verrez quelque chose comme ça :)
table = None, signifie qu'il prendra le nom de table par défaut, qui dans ce cas sera app2_yourmodel.
PS lors de la migration, il verra que l'application content_type app1.yourmodel a été supprimée et peut être supprimée. Vous pouvez dire oui à cela, mais seulement si vous ne l'utilisez pas. Si vous en dépendez beaucoup pour que les FK de ce type de contenu soient intacts, ne répondez pas encore par oui ou par non, mais allez dans la base de données cette fois-ci manuellement, supprimez le contentype app2.yourmodel et renommez le contenttype app1. yourmodel à app2.yourmodel, puis continuez en répondant non.
la source
app_label = 'app1'
option meta.field1 = models.ForeignKey('app1.myModel').
lorsque je migre, j'obtiens un ValueError indiquant quefield1 was declared with a lazy reference to 'app1.myModel' but app 'app1' doesn't provide model 'MyModel'
Je reçois des migrations de codage manuel nerveuses (comme l'exige la réponse d' Ozan ), donc ce qui suit combine les stratégies d' Ozan et de Michael pour minimiser la quantité de codage manuel requise:
makemigrations
.app1
versapp2
Comme recommandé par @Michael, nous pointons le nouveau modèle vers l'ancienne table de base de données en utilisant l'
db_table
option Meta sur le "nouveau" modèle:Courez
makemigrations
. Cela généreraCreateModel
dansapp2
etDeleteModel
dansapp1
. Techniquement, ces migrations se réfèrent exactement à la même table et supprimeraient (y compris toutes les données) et recréeraient la table.En réalité, nous ne voulons (ou n'avons pas besoin) de faire quoi que ce soit à la table. Nous avons juste besoin de Django pour croire que le changement a été fait. Selon la réponse de @ Ozan, le
state_operations
drapeauSeparateDatabaseAndState
fait cela. Nous enveloppons donc toutes lesmigrations
entrées DANS LES DEUX FICHIERS DE MIGRATIONS avecSeparateDatabaseAndState(state_operations=[...])
. Par exemple,devient
Vous devez également vous assurer que la nouvelle
CreateModel
migration «virtuelle» dépend de toute migration qui a réellement créé ou modifié la table d'origine . Par exemple, si vos nouvelles migrations sontapp2.migrations.0004_auto_<date>
(pour leCreate
) etapp1.migrations.0007_auto_<date>
(pour leDelete
), la chose la plus simple à faire est:app1.migrations.0007_auto_<date>
et copiez saapp1
dépendance (par exemple('app1', '0006...'),
). Il s'agit de la migration «immédiatement antérieure» versapp1
et doit inclure des dépendances sur toute la logique de création de modèle réelle.app2.migrations.0004_auto_<date>
et ajoutez la dépendance que vous venez de copier à sadependencies
liste.Si vous avez des
ForeignKey
relations avec le modèle que vous déplacez, ce qui précède peut ne pas fonctionner. Cela se produit parce que:ForeignKey
modificationsForeignKey
modificationsstate_operations
, nous devons donc nous assurer qu'elles sont distinctes des opérations de table.REMARQUE: Django 2.2 a ajouté un avertissement (
models.E028
) qui rompt cette méthode. Vous pourrez peut-être contourner ce problème,managed=False
mais je ne l'ai pas testé.L'ensemble d'opérations «minimum» diffère selon la situation, mais la procédure suivante devrait fonctionner pour la plupart / toutes les
ForeignKey
migrations:app1
àapp2
, définissezdb_table
, mais NE changez aucune référence FK.makemigrations
et encapsulez toutes lesapp2
migrationsstate_operations
(voir ci-dessus)app2
CreateTable
la dernièreapp1
migrationmodels.py
(NE PAS le supprimer) afin qu'il ne soit pas en concurrence avec la classe importée.Exécutez
makemigrations
mais NE PAS encapsuler quoi que ce soitstate_operations
(les changements FK devraient réellement se produire). Ajoutez une dépendance dans toutes lesForeignKey
migrations (c'estAlterField
-à- dire ) à laCreateTable
migration versapp2
(vous aurez besoin de cette liste pour l'étape suivante, alors gardez-en une trace). Par exemple:CreateModel
par exempleapp2.migrations.0002_auto_<date>
et copiez le nom de cette migration.Trouvez toutes les migrations qui ont une ForeignKey vers ce modèle (par exemple en recherchant
app2.YourModel
des migrations comme:Ajoutez la
CreateModel
migration en tant que dépendance:Supprimer les modèles de
app1
makemigrations
et encapsulez laapp1
migrationstate_operations
.ForeignKey
migrations (c'est-à-direAlterField
) de l'étape précédente (peut inclure des migrations dansapp1
etapp2
).DeleteTable
dépendait déjà desAlterField
migrations, donc je n'avais pas besoin de les appliquer manuellement (c'est-à-direAlter
avantDelete
).À ce stade, Django est prêt à partir. Le nouveau modèle pointe vers l'ancienne table et les migrations de Django l'ont convaincu que tout a été déplacé de manière appropriée. La grande mise en garde (de la réponse de @ Michael) est qu'un nouveau
ContentType
est créé pour le nouveau modèle. Si vousForeignKey
créez un lien (par exemple par ) vers des types de contenu, vous devrez créer une migration pour mettre à jour laContentType
table.Je voulais nettoyer après moi (options Meta et noms de table) alors j'ai utilisé la procédure suivante (de @Michael):
db_table
entrée Metamakemigrations
nouveau pour générer le renommage de la base de donnéesDeleteTable
migration. Cela ne semble pas nécessaire car celaDelete
devrait être purement logique, mais j'ai rencontré des erreurs (par exemple, celaapp1_yourmodel
n'existe pas) si je ne le fais pas.la source
table_name: (models.E028) db_table 'table_name' is used by multiple models: app1.Model, app2.Model.
managed=False
mais je ne suis pas en mesure de vérifier.Une autre alternative hacky si les données ne sont pas volumineuses ou trop compliquées, mais toujours importantes à maintenir, est de:
la source
Copié de ma réponse à https://stackoverflow.com/a/47392970/8971048
Si vous avez besoin de déplacer le modèle et que vous n'avez plus accès à l'application (ou que vous ne voulez plus l'accès), vous pouvez créer une nouvelle opération et envisager de créer un nouveau modèle uniquement si le modèle migré ne le fait pas exister.
Dans cet exemple, je passe «MyModel» de old_app à myapp.
la source
Ceci est testé grossièrement, alors n'oubliez pas de sauvegarder votre DB !!!
Par exemple, il existe deux applications:
src_app
etdst_app
, nous voulons déplacer le modèleMoveMe
desrc_app
versdst_app
.Créez des migrations vides pour les deux applications:
Supposons que les nouvelles migrations soient
XXX1_src_app_new
etXXX1_dst_app_new
, les principales migrations précédentes sontXXX0_src_app_old
etXXX0_dst_app_old
.Ajoutez une opération qui renomme la table du
MoveMe
modèle et renomme son app_label dans ProjectState enXXX1_dst_app_new
. N'oubliez pas d'ajouter une dépendance à laXXX0_src_app_old
migration. LaXXX1_dst_app_new
migration qui en résulte est:Ajoutez une dépendance
XXX1_dst_app_new
àXXX1_src_app_new
.XXX1_src_app_new
est une migration sans opération nécessaire pour s'assurer que les futuressrc_app
migrations seront exécutées aprèsXXX1_dst_app_new
.Passer
MoveMe
desrc_app/models.py
àdst_app/models.py
. Puis exécutez:C'est tout!
la source
Vous pouvez essayer ce qui suit (non testé):
src_app
versdest_app
dest_app
; assurez-vous que la migration du schéma dépend de la dernièresrc_app
migration ( https://docs.djangoproject.com/en/dev/topics/migrations/#migration-files )dest_app
, qui copie toutes les données desrc_app
src_app
; assurez-vous que la migration de schéma dépend de la dernière migration (de données) dedest_app
- c'est-à-dire: la migration de l'étape 3Notez que vous allez copier la table entière, au lieu de la déplacer , mais de cette façon, les deux applications n'ont pas à toucher une table qui appartient à l'autre application, ce qui, à mon avis, est plus important.
la source
Disons que vous déplacez le modèle TheModel de app_a vers app_b.
Une autre solution consiste à modifier manuellement les migrations existantes. L'idée est que chaque fois que vous voyez une opération modifiant TheModel dans les migrations d'app_a, vous copiez cette opération à la fin de la migration initiale d'app_b. Et chaque fois que vous voyez une référence 'app_a.TheModel' dans les migrations d'app_a, vous la changez en 'app_b.TheModel'.
Je viens de faire cela pour un projet existant, dans lequel je voulais extraire un certain modèle vers une application réutilisable. La procédure s'est bien déroulée. Je suppose que les choses seraient beaucoup plus difficiles s'il y avait des références de app_b à app_a. De plus, j'avais une table Meta.db_table définie manuellement pour mon modèle, ce qui aurait pu aider.
Vous vous retrouverez notamment avec un historique de migration modifié. Cela n'a pas d'importance, même si vous avez une base de données avec les migrations d'origine appliquées. Si les migrations d'origine et réécrites se retrouvent avec le même schéma de base de données, une telle réécriture devrait être correcte.
la source
Faites-le individuellement pour chaque modèle qui doit être déplacé. Je ne suggérerais pas de faire ce que l'autre réponse dit en passant aux entiers et en revenant aux clés étrangères Il y a une chance que les nouvelles clés étrangères soient différentes et que les lignes puissent avoir des ID différents après les migrations et je ne voulais pas courir de risque des identifiants incompatibles lors du retour aux clés étrangères.
la source