Question

Je prévois de renommer plusieurs modèles dans un projet Django existant où il existe de nombreux autres modèles ayant des relations de clé étrangère 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.

Disons que je commence avec les modèles suivants dans 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 veux renommer le Foo modèle parce que le nom n'a pas vraiment de sens et provoque de la confusion dans le code, et Bar cela donnerait un nom beaucoup plus clair.

D'après ce que j'ai lu dans la documentation de développement de Django, je suppose la stratégie de migration suivante :

Étape 1

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()

Noter la AnotherModel nom du champ pour foo ne change pas, mais la relation est mise à jour vers le Bar modèle.Mon raisonnement est que je ne devrais pas trop changer d'un coup et que si je changeais ce nom de champ en bar Je risquerais de perdre les données de cette colonne.

Étape 2

Créez une migration vide :

python manage.py makemigrations --empty myapp

Étape 3

Modifier le Migration classe dans le fichier de migration créé à l'étape 2 pour ajouter la RenameModel opération à la liste des opérations :

class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0001_initial'),
    ]

    operations = [
        migrations.RenameModel('Foo', 'Bar')
    ]

Étape 4

Appliquez la migration :

python manage.py migrate

Étape 5

Modifiez 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()

Étape 6

Créez une autre migration vide :

python manage.py makemigrations --empty myapp

Étape 7

Modifier le Migration classe dans le fichier de migration créé à l'étape 6 pour ajouter la RenameField opération(s) pour tous les noms de champs 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')
    ]

Étape 8

Appliquez la 2ème migration :

python manage.py migrate

Outre la mise à jour du reste du code (vues, formulaires, etc.) pour refléter les nouveaux noms de variables, est-ce essentiellement ainsi que fonctionnerait la nouvelle fonctionnalité de migration ?

De plus, cela semble représenter beaucoup d'étapes.Les opérations de migration peuvent-elles être condensées d’une manière ou d’une autre ?

Merci!

Était-ce utile?

La solution

Donc, quand j'ai essayé ceci, 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 pouvez obtenir des erreurs si vous ne mettez pas à jour les noms là où ils sont importés, par ex.admin.py et des fichiers de migration encore plus anciens (!).

Mise à jour:Comme César 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 essaie manage.py makemigrations d'abord, puis vérifiez le fichier de migration.

Autres conseils

Au début, je pensais que la méthode Fiver fonctionnait pour moi car la migration a bien fonctionné jusqu'à l'étape 4.Cependant, les modifications implicites de « ForeignKeyField(Foo) » en « ForeignKeyField(Bar) » n’étaient liées à aucune migration.C'est pourquoi la migration a échoué lorsque j'ai voulu renommer les champs de relation (étapes 5 à 8).Cela peut être dû au fait que mes « AnotherModel » et « YetAnotherModel » sont distribués dans d'autres applications dans mon cas.

J'ai donc réussi à renommer mes modèles et mes champs de relation en suivant les étapes ci-dessous :

J'ai adapté la méthode de ce et surtout le truc d'otranzer.

Alors comme Fiver, disons que nous avons monapplication:

class Foo(models.Model):
    name = models.CharField(unique=True, max_length=32)
    description = models.TextField(null=True, blank=True)

Et en monautreapplication:

class AnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_awesome = models.BooleanField()


class YetAnotherModel(models.Model):
    foo = models.ForeignKey(Foo)
    is_ridonkulous = models.BooleanField()

Étape 1:

Transformez chaque OneToOneField(Foo) ou ForeignKeyField(Foo) en IntegerField().(Cela conservera l'identifiant 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()

Alors

python manage.py makemigrations

python manage.py migrate

Étape 2:(Comme les étapes 2 à 4 de Fiver)

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

Étape 3:

Transformez votre IntegerField() en son précédent ForeignKeyField ou OneToOneField mais avec le nouveau modèle de barre.(Le champ entier précédent stockait l'identifiant, donc Django comprend cela 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 sur les migrations RenameModel Foo-> Bar.Ainsi, si AnotherModel et YetAnotherModel se trouvent 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')
        ),
    ]

Alors

python manage.py migrate

Étape 4:

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()

puis renommer automatiquement

python manage.py makemigrations

(Django devrait vous demander si vous avez réellement 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 d'un seul coup (c'est-à-dire l'étape 1 et l'étape 5 ensemble).Ensuite, j'ai créé une migration de schéma mais je l'ai modifiée 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 Bar bien.

d'ici: https://hanmir.wordpress.com/2012/08/30/rename-model-django-south-migration/

Pour Django 1.10, j'ai réussi à changer deux noms de classe de modèle (dont une ForeignKey et avec des données) en exécutant simplement Makemigrations, puis Migrate pour l'application.Pour l'étape Makemigrations, j'ai dû confirmer que je souhaitais changer les noms des tables.Migrate a modifié les noms des tables sans problème.

Ensuite, j'ai changé le nom du champ ForeignKey pour qu'il corresponde, et Makemigrations m'a encore demandé de confirmer que je voulais changer le nom.Migrez puis effectuez le changement.

J'ai donc procédé en deux étapes sans aucune modification particulière des fichiers.J'ai eu des erreurs au début parce que j'ai oublié de modifier le fichier admin.py, comme mentionné par @wasibigeek.

J'ai également été confronté au problème décrit par V. Thorey et j'ai trouvé que son approche est très utile mais peut être condensée en moins d'étapes qui sont en fait les étapes 5 à 8 comme Fiver l'a décrit sans les étapes 1 à 4, sauf que l'étape 7 doit être modifiée comme mon en dessous de l'étape 3.Les étapes globales sont les suivantes :

Étape 1:Modifiez 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()

Étape 2:Créer une migration vide

python manage.py makemigrations --empty myapp

Étape 3:Modifiez la classe Migration dans le fichier de migration créé à l'étape 2

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')
]

Étape 4:Appliquer la migration

python manage.py migrate

Fait

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 à Newname Run python manage.py makemigrations.Il vous demanderaDid you rename the appname.oldName model to NewName? [y/N] sélectionnez O

Courir python manage.py migrate et il vous demandera

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.Êtes-vous sûr de vouloir supprimer ces types de contenu ?Si vous n'êtes pas sûr, répondez « non ».

Type 'yes' to continue, or 'no' to cancel: Select No

Il renomme et migre toutes les données existantes vers une nouvelle table nommée pour moi.

Malheureusement, j'ai trouvé des problèmes (chaque Django 1.x) avec la migration des renommages qui laissent les anciens noms de tables dans la base de données.

Django n'essaie même rien sur l'ancienne table, il suffit de renommer son propre modèle.Le même problème avec les clés étrangères et les index en général - les modifications ne sont pas correctement suivies par Django.

La solution la plus simple (contournement) :

class Foo(models.Model):
     name = models.CharField(unique=True, max_length=32)
     ...
Bar = Foo  # and use Bar only

La vraie solution (un moyen facile de changer tous les indices, contraintes, déclencheurs, noms, etc. dans 2 commet, mais plutôt pour plus petit les tables):

commettre A :

  1. créer le même modèle comme l'ancien
# deprecated - TODO: TO BE REMOVED
class Foo(model.Model):
    ...

class Bar(model.Model):
    ...
  1. changer de code pour fonctionner avec le nouveau modèle Bar seulement.(y compris toutes les relations sur le schéma)

En migration, préparez-vous RunPython, qui copie les données de FOO à barre (y compris id de Foo)

  1. optimisation facultative (si nécessaire pour des tables plus grandes)

commettre B : (pas de précipitation, faites-le lorsqu'une équipe entière est migrée)

  1. chute en toute sécurité de l'ancien modèle Foo

nettoyage supplémentaire :

  • courge sur les migrations

bug dans Django :

J'avais besoin de renommer quelques tables.Mais un seul changement de nom de modèle a été remarqué par Django.Cela s'est produit parce que Django itère sur les modèles ajoutés, puis supprimés.Pour chaque paire, il vérifie s'ils appartiennent à la même application et ont champs identiques.Une seule table n'avait pas de clé étrangère pour les tables à renommer (les clés étrangères contiennent le nom de la classe modèle, comme vous vous en souvenez).En d’autres termes, une seule table ne présentait aucun changement de champ.C'est pourquoi cela a été remarqué.

La solution consiste donc à renommer une table à la fois, en changeant le nom de la classe de modèle dans models.py, peut-être views.py, et effectuer une migration.Après cela, inspectez votre code pour d'autres références (noms de classe de modèle, noms (de requête) associés, noms de variables).Effectuez une migration, si nécessaire.Ensuite, combinez éventuellement toutes ces migrations en une seule (assurez-vous également de copier les importations).

Je ferais des mots à @ceasaro, les miens sur son commentaire à ce sujet répondre.

Les versions plus récentes de Django peuvent détecter les changements et demander ce qui a été fait.J'ajouterais également que Django pourrait mélanger l'ordre d'exécution de certaines commandes de migration.

Il serait sage d'appliquer de petits changements et d'exécuter makemigrations et migrate et 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.

Je voulais juste confirmer et ajouter un commentaire sur Ceasaro.Django 2.0 semble le faire automatiquement maintenant.

Je suis sur Jango 2.2.1, il ne me restait plus qu'à renommer le modèle et lancer makemigrations.

Ici, il me demande si j'avais renommé la classe spécifique de A à B, j'ai choisi oui et j'ai exécuté migrate et tout semble fonctionner.

Remarque Je n'ai renommé l'ancien nom du modèle dans aucun fichier du dossier projet/migrations.

Si vous utilisez un bon IDE comme PyCharm, vous pouvez cliquer avec le bouton droit sur le nom du modèle et effectuer un refactor -> renommer.Cela vous évite d'avoir à parcourir tout votre code qui fait référence au modèle.Ensuite, exécutez makemigrations et migrez.Django 2+ confirmera simplement le changement de nom.

J'ai mis à jour Django de la version 10 vers la version 11 :

sudo pip install -U Django

(-U pour "mise à niveau") et cela a résolu le problème.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top