Je prévois de renommer plusieurs modèles dans un projet Django existant dans lequel de nombreux autres modèles ont des relations de clé étrangères avec les modèles que je souhaite renommer. Je suis presque certain que cela nécessitera plusieurs migrations, mais je ne suis pas sûr de la procédure exacte.
Supposons que je commence avec les modèles suivants au sein d'une application Django appelée myapp
:
class Foo(models.Model):
name = models.CharField(unique=True, max_length=32)
description = models.TextField(null=True, blank=True)
class AnotherModel(models.Model):
foo = models.ForeignKey(Foo)
is_awesome = models.BooleanField()
class YetAnotherModel(models.Model):
foo = models.ForeignKey(Foo)
is_ridonkulous = models.BooleanField()
Je souhaite renommer le modèle Foo
car le nom n'a pas vraiment de sens et est source de confusion dans le code. Bar
rendrait le nom beaucoup plus clair.
D'après ce que j'ai lu dans la documentation de développement de Django, j'assume la stratégie de migration suivante:
Modifier models.py
:
class Bar(models.Model): # <-- changed model name
name = models.CharField(unique=True, max_length=32)
description = models.TextField(null=True, blank=True)
class AnotherModel(models.Model):
foo = models.ForeignKey(Bar) # <-- changed relation, but not field name
is_awesome = models.BooleanField()
class YetAnotherModel(models.Model):
foo = models.ForeignKey(Bar) # <-- changed relation, but not field name
is_ridonkulous = models.BooleanField()
Notez que le nom du champ AnotherModel
pour foo
ne change pas, mais la relation est mise à jour vers le modèle Bar
. Mon raisonnement est que je ne devrais pas trop changer en même temps et que si je changeais le nom de ce champ en bar
, je risquerais de perdre les données de cette colonne.
Créez une migration vide:
python manage.py makemigrations --empty myapp
Modifiez la classe Migration
dans le fichier de migration créé à l'étape 2 pour ajouter l'opération RenameModel
à la liste des opérations:
class Migration(migrations.Migration):
dependencies = [
('myapp', '0001_initial'),
]
operations = [
migrations.RenameModel('Foo', 'Bar')
]
Appliquer la migration:
python manage.py migrate
Editez les noms de champs associés dans models.py
:
class Bar(models.Model):
name = models.CharField(unique=True, max_length=32)
description = models.TextField(null=True, blank=True)
class AnotherModel(models.Model):
bar = models.ForeignKey(Bar) # <-- changed field name
is_awesome = models.BooleanField()
class YetAnotherModel(models.Model):
bar = models.ForeignKey(Bar) # <-- changed field name
is_ridonkulous = models.BooleanField()
Créez une autre migration vide:
python manage.py makemigrations --empty myapp
Modifiez la classe Migration
dans le fichier de migration créé à l'étape 6 pour ajouter la ou les opérations RenameField
de tous les noms de champ associés à la liste des opérations:
class Migration(migrations.Migration):
dependencies = [
('myapp', '0002_rename_fields'), # <-- is this okay?
]
operations = [
migrations.RenameField('AnotherModel', 'foo', 'bar'),
migrations.RenameField('YetAnotherModel', 'foo', 'bar')
]
Appliquez la 2ème migration:
python manage.py migrate
Outre la mise à jour du reste du code (vues, formulaires, etc.) afin de refléter les nouveaux noms de variable, s'agit-il en gros de la manière dont la nouvelle fonctionnalité de migration fonctionnerait?
En outre, cela semble être beaucoup d'étapes. Les opérations de migration peuvent-elles être condensées d'une manière ou d'une autre?
Merci!
Donc, lorsque j'ai essayé cela, il semble que vous puissiez condenser les étapes 3 à 7:
class Migration(migrations.Migration):
dependencies = [
('myapp', '0001_initial'),
]
operations = [
migrations.RenameModel('Foo', 'Bar'),
migrations.RenameField('AnotherModel', 'foo', 'bar'),
migrations.RenameField('YetAnotherModel', 'foo', 'bar')
]
Vous risquez d’obtenir des erreurs si vous ne mettez pas à jour les noms là où il est importé, par exemple. admin.py et même des fichiers de migration plus anciens (!).
Update: Comme ceasaro mentionne, les versions plus récentes de Django sont généralement capables de détecter et de demander si un modèle est renommé. Alors essayez d'abord manage.py makemigrations
puis vérifiez le fichier de migration.
Au début, je pensais que la méthode de Fiver fonctionnait pour moi, car la migration avait bien fonctionné jusqu'à l'étape 4. Cependant, les modifications implicites «ForeignKeyField (Foo)» dans «ForeignKeyField (Bar)» n'étaient associées à aucune migration. C'est pourquoi la migration a échoué lorsque j'ai voulu renommer les champs de relation (étapes 5 à 8) . Cela est peut-être dû au fait que mon 'AnotherModel' et 'YetAnotherModel' sont distribués dans d'autres applications dans mon cas.
J'ai donc réussi à renommer mes modèles et champs de relation en procédant comme suit:
J'ai adapté la méthode de this et particulièrement le tour d'otranzer.
Donc, comme Fiver, disons que nous avons dans myapp:
class Foo(models.Model):
name = models.CharField(unique=True, max_length=32)
description = models.TextField(null=True, blank=True)
Et dans myotherapp:
class AnotherModel(models.Model):
foo = models.ForeignKey(Foo)
is_awesome = models.BooleanField()
class YetAnotherModel(models.Model):
foo = models.ForeignKey(Foo)
is_ridonkulous = models.BooleanField()
Transformez chaque OntToOneField (Foo) ou ForeignKeyField (Foo) en IntegerField (). (Ceci gardera l'id de l'objet Foo associé comme valeur du champ entier).
class AnotherModel(models.Model):
foo = models.IntegerField()
is_awesome = models.BooleanField()
class YetAnotherModel(models.Model):
foo = models.IntegerField()
is_ridonkulous = models.BooleanField()
Ensuite
python manage.py makemigrations
python manage.py migrate
Changer le nom du modèle
class Bar(models.Model): # <-- changed model name
name = models.CharField(unique=True, max_length=32)
description = models.TextField(null=True, blank=True)
Créez une migration vide:
python manage.py makemigrations --empty myapp
Puis éditez le comme:
class Migration(migrations.Migration):
dependencies = [
('myapp', '0001_initial'),
]
operations = [
migrations.RenameModel('Foo', 'Bar')
]
Finalement
python manage.py migrate
Transformez votre IntegerField () en arrière dans son ancien ForeignKeyField, OneToOneField mais avec le nouveau modèle de barre. (Le champ entier précédent stockait l'identifiant, donc Django l'a compris et rétablit la connexion, ce qui est cool.)
class AnotherModel(models.Model):
foo = models.ForeignKey(Bar)
is_awesome = models.BooleanField()
class YetAnotherModel(models.Model):
foo = models.ForeignKey(Bar)
is_ridonkulous = models.BooleanField()
Alors fais:
python manage.py makemigrations
Très important, à cette étape, vous devez modifier chaque nouvelle migration et ajouter la dépendance aux migrations RenameModel Foo-> Bar . Ainsi, si AnotherModel et YetAnotherModel sont tous deux dans myotherapp, la migration créée dans myotherapp doit ressembler à ceci:
class Migration(migrations.Migration):
dependencies = [
('myapp', '00XX_the_migration_of_myapp_with_renamemodel_foo_bar'),
('myotherapp', '00xx_the_migration_of_myotherapp_with_integerfield'),
]
operations = [
migrations.AlterField(
model_name='anothermodel',
name='foo',
field=models.ForeignKey(to='myapp.Bar'),
),
migrations.AlterField(
model_name='yetanothermodel',
name='foo',
field=models.ForeignKey(to='myapp.Bar')
),
]
Ensuite
python manage.py migrate
Finalement, vous pouvez renommer vos champs
class AnotherModel(models.Model):
bar = models.ForeignKey(Bar) <------- Renamed fields
is_awesome = models.BooleanField()
class YetAnotherModel(models.Model):
bar = models.ForeignKey(Bar) <------- Renamed fields
is_ridonkulous = models.BooleanField()
et ensuite renommer automatiquement
python manage.py makemigrations
(Django devrait vous demander si vous avez renommé le nom du modèle, dites oui)
python manage.py migrate
Et c'est tout!
Cela fonctionne sur Django1.8
Je devais faire la même chose. J'ai changé le modèle en une seule fois (c'est-à-dire les étapes 1 et 5 ensemble). Puis créé une migration de schéma mais modifié comme suit:
class Migration(SchemaMigration):
def forwards(self, orm):
db.rename_table('Foo','Bar')
def backwards(self, orm):
db.rename_table('Bar','Foo')
Cela a parfaitement fonctionné. Toutes mes données existantes sont apparues, toutes les autres tables référencées sont correctes.
à partir d'ici: https://hanmir.wordpress.com/2012/08/30/rename-model-Django-south-migration/
Pour Django 1.10, j'ai réussi à modifier deux noms de classe de modèle (y compris une clé étrangère et des données) en exécutant simplement Makemigrations, puis Migrate pour l'application. Pour l'étape Makemigrations, je devais confirmer que je voulais changer les noms de table. Migrer a changé les noms des tables sans problème.
Ensuite, j'ai modifié le nom du champ ForeignKey afin qu'il corresponde, et Makemigrations a demandé à nouveau de confirmer que je souhaitais changer le nom. Migrer que fait le changement.
J'ai donc pris cela en deux étapes sans aucune édition de fichier spéciale. J'ai eu des erreurs au début parce que j'avais oublié de changer le fichier admin.py, comme mentionné par @wasibigeek.
J'ai également rencontré le problème décrit par v.thorey et constaté que son approche était très utile mais qu'elle pouvait être condensée en moins d'étapes. ci-dessous étape 3. Les étapes globales sont les suivantes:
class Bar(models.Model):
name = models.CharField(unique=True, max_length=32)
description = models.TextField(null=True, blank=True)
class AnotherModel(models.Model):
bar = models.ForeignKey(Bar) # <-- changed field name
is_awesome = models.BooleanField()
class YetAnotherModel(models.Model):
bar = models.ForeignKey(Bar) # <-- changed field name
is_ridonkulous = models.BooleanField()
python manage.py makemigrations --empty myapp
class Migration(migrations.Migration):
dependencies = [
('myapp', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='AnotherModel',
name='foo',
field=models.IntegerField(),
),
migrations.AlterField(
model_name='YetAnotherModel',
name='foo',
field=models.IntegerField(),
),
migrations.RenameModel('Foo', 'Bar'),
migrations.AlterField(
model_name='AnotherModel',
name='foo',
field=models.ForeignKey(to='myapp.Bar'),
),
migrations.AlterField(
model_name='YetAnotherModel',
name='foo',
field=models.ForeignKey(to='myapp.Bar'),
),
migrations.RenameField('AnotherModel', 'foo', 'bar'),
migrations.RenameField('YetAnotherModel', 'foo', 'bar')
]
python manage.py migrate
P.S. J'ai essayé cette approche sur Django 1.9
J'utilise Django version 1.9.4
J'ai suivi les étapes suivantes: -
Je viens de renommer le modèle oldName en NewName Exécutez python manage.py makemigrations
. Il vous demandera Did you rename the appname.oldName model to NewName? [y/N]
sélectionnez Y
Exécutez python manage.py migrate
et il vous demandera de
Les types de contenu suivants sont obsolètes et doivent être supprimés:
appname | oldName
appname | NewName
Tous les objets liés à ces types de contenu par une clé étrangère seront également supprimés. Etes-vous sûr de vouloir supprimer ces types de contenu?.
Type 'yes' to continue, or 'no' to cancel: Select No
Il renommer et migrer toutes les données existantes à la nouvelle table nommée pour moi.
Malheureusement, j'ai rencontré des problèmes (chaque Django 1.x) avec la migration de renommage qui laissait les anciens noms de table dans la base de données (n'essayez même pas sur l'ancienne table, renommez juste modèle).
Solution pour ce cas:
class Foo(models.Model):
name = models.CharField(unique=True, max_length=32)
...
Bar = Foo
Je devais renommer quelques tables. Mais Django n'a remarqué qu'un seul changement de modèle. Cela est dû au fait que Django itère sur les modèles ajoutés, puis supprimés. Pour chaque paire, il vérifie si elles sont de la même application et ont champs identiques . Une seule table ne contenait aucune clé étrangère à des tables à renommer (les clés étrangères contiennent le nom de la classe de modèle, comme vous vous en souvenez). En d'autres termes, une seule table n'avait aucun changement de champ. C'est pourquoi cela a été remarqué.
La solution consiste donc à renommer une table à la fois, en modifiant le nom de la classe de modèle dans models.py
, éventuellement views.py
, et en effectuant une migration. Après cela, inspectez votre code pour d’autres références (noms de classe de modèle, noms associés (de requête), noms de variables). Effectuer une migration, si nécessaire. Ensuite, combinez éventuellement toutes ces migrations en une seule (assurez-vous également de copier les importations).
Je voulais juste confirmer et ajouter un commentaire ceasaro. Django 2.0 semble le faire automatiquement maintenant.
Je suis sur Jango 2.2.1, tout ce que je devais faire pour renommer le modèle et exécuter makemigrations.
Ici, il me demande si j'ai renommé la classe spécifique de A à B, j'ai choisi oui, j'ai migré et tout semble fonctionner.
Remarque Je n'ai renommé l'ancien nom de modèle dans aucun fichier du dossier project/migrations.
Je voudrais faire @ceasaro mots, le mien sur son commentaire sur ce répondre .
Les versions les plus récentes de Django peuvent détecter les modifications et demander ce qui a été fait ... Je voudrais également ajouter que Django peut mélanger l'ordre d'exécution de certaines commandes de migration.
Il serait sage d'appliquer de petites modifications et d'exécuter makemigrations
et migrate
. Si l'erreur se produit, le fichier de migration peut être modifié.
L'ordre d'exécution de certaines lignes peut être modifié pour éviter les erreurs.
Si vous utilisez un bon IDE comme PyCharm, vous pouvez cliquer avec le bouton droit sur le nom du modèle et faire un refactor -> renommer. Cela vous évite d'avoir à parcourir tout votre code faisant référence au modèle. Ensuite, lancez makemigrations et migrez. Django 2+ confirmera simplement le changement de nom.