migration de django-champ model-changement de nom sans perte de données

j'ai un projet django avec une table de base de données qui contient déjà des données. J'aimerais changer le nom du champ sans perdre aucune des données de cette colonne. Mon plan initial était simplement de changer le nom du champ model d'une manière qui ne modifierait pas réellement le nom de la table db (en utilisant le paramètre db_column colonne):

le modèle original:

class Foo(models.Model):
  orig_name = models.CharField(max_length=50)

le nouveau modèle:

class Foo(models.Model):
  name = models.CharField(max_length=50, db_column='orig_name')

mais, l'exécution du schemamigration --auto de South produit un script de migration qui supprime la colonne originale, orig_name , et ajoute une nouvelle colonne, name , qui aurait l'effet secondaire indésirable de supprimer les données dans cette colonne. (Je suis également confus quant à la raison pour laquelle le Sud veut changer le nom de la colonne dans le db, puisque ma compréhension de db_column était qu'il permet un changement au nom du champ model sans changer le nom de la colonne de table de base de données).

si Je ne peux pas s'en tirer en changeant le champ model sans changer le champ db, je pense que je pourrais faire un changement de nom plus simple comme ceci:

le modèle original:

class Foo(models.Model):
  orig_name = models.CharField(max_length=50)

le nouveau modèle:

class Foo(models.Model):
  name = models.CharField(max_length=50)

quelle que soit la stratégie que j'utilise (je préférerais la première, mais je trouverais la deuxième acceptable), mon principal souci est de m'assurer de ne pas perdre les données qui sont déjà dans cette colonne.

cela nécessite-t-il un processus en plusieurs étapes? (par exemple 1. ajout d'une colonne, 2. la migration des données de l'ancienne colonne de la nouvelle colonne, et 3. retrait de la colonne d'origine) Ou Puis-je modifier le script de migration avec quelque chose comme db.alter_column ?

Quelle est la meilleure façon de conserver les données dans cette colonne tout en changeant le nom de la colonne?

26
demandé sur sfletche 2010-08-17 03:09:19

6 réponses

c'est assez facile à réparer. Mais vous devrez modifier la migration vous-même.

au lieu de laisser tomber et d'ajouter la colonne, utilisez db.rename_column . Vous pouvez simplement modifier la migration créée par schemamigration --auto

16
répondu Wolph 2010-08-16 23:14:07

changer le nom du champ tout en conservant le champ DB

ajout d'une réponse pour Django 1.8+ (avec Django-migrations indigènes, plutôt que Sud).

effectue une migration qui ajoute d'abord une propriété db_column , puis renomme le champ. Django comprend que le premier est un no-op (parce qu'il change le db_column pour rester le même), et que le second est un no-op (parce qu'il ne fait pas de changements de schéma). J'ai examiné le journal pour voir qu'il n'y avait aucun changement de schéma...

operations = [
    migrations.AlterField(
        model_name='mymodel',
        name='oldname',
        field=models.BooleanField(default=False, db_column=b'oldname'),
    ),
    migrations.RenameField(
        model_name='mymodel',
        old_name='oldname',
        new_name='newname',
    ),
]
20
répondu Amichai Schreiber 2015-10-17 21:10:56

j'ai rencontré cette situation. Je voulais changer les noms des champs dans le model mais garder les noms des colonnes identiques.

la façon dont je l'ai fait est de faire schemamigration --empty [app] [some good name for the migration] . Le problème est qu'en ce qui concerne le Sud, changer les noms de champ dans le modèle est un changement qu'il doit gérer. Il faut donc créer une migration. Toutefois, nous savons qu'il n'y a rien à faire du côté de la base de données. donc une migration vide évite de faire des opérations inutiles sur la base de données et satisfait au besoin du Sud de gérer ce qu'il considère être un changement.

notez que si vous utilisez loaddata ou utilisez L'installation D'essai de Django (qui utilise loaddata dans les coulisses). Vous devrez mettre à jour les fixtures pour utiliser le nouveau nom de champ parce que les fixtures sont basées sur les noms de champ du modèle, pas les noms de champ de la base de données.

pour les cas où les noms de colonne ne changement dans la base de données, Je ne recommande jamais l'utilisation de db.rename_column pour les migrations de colonne. J'utilise la méthode décrite par sjh dans cette réponse :

j'ai ajouté la nouvelle colonne comme un schéma, puis créé un datamigration pour déplacer des valeurs dans le nouveau champ, puis un deuxième schéma pour supprimer l'ancienne colonne

comme je l'ai noté dans un commentaire sur cette question, le le problème avec db.rename_column est qu'il ne renomme pas les contraintes avec la colonne. Je ne sais pas si la question est purement superficielle ou si cela signifie qu'une future migration peut échouer parce qu'elle ne peut pas trouver de contrainte.

4
répondu Louis 2017-05-23 11:33:26

en fait avec Django 1.10, il suffit de renommer le champ dans le modèle et ensuite en lançant makemigrations, identifie immédiatement l'opération(i.e. un champ disparaît, un autre apparaît à sa place):

$ ./manage.py makemigrations
Did you rename articlerequest.update_at to articlerequest.updated_at (a DateTimeField)? [y/N] y
Migrations for 'article_requests':
  article_requests/migrations/0003_auto_20160906_1623.py:
    - Rename field update_at on articlerequest to updated_at
3
répondu brokenthorn 2016-09-07 08:14:47

j'ai rencontré cette situation sur Django 1.7.7. J'ai fini par faire ce qui suit qui a fonctionné pour moi.

./manage.py makemigrations <app_name> --empty

a ajouté une sous-classe simple de migrations.RenameField qui ne touche pas la base de données:

class RenameFieldKeepDatabaseColumn(migrations.RenameField):
def database_backwards(self, app_label, schema_editor, from_state, to_state):
    pass

def database_forwards(self, app_label, schema_editor, from_state, to_state):
    pass
1
répondu alphanumeric0 2015-11-10 21:16:18

il est possible de renommer un champ sans faire d'édition manuelle de fichier de migration:

  1. ajouter db_column= OLD_FIELD_NAME au champ original.
  2. Run: python3 manage.py makemigrations
  3. renommer le champ de OLD_FIELD_NAME à NEW_FIELD_NAME
  4. Run: python3 manage.py makemigrations

Vous serez invité:

avez-vous renommé modèle . OLD_FIELD_NAME à MODÈLE . NEW_FIELD_NAME (une clé étrangère)? [y/N] y

cela générera deux fichiers de migration plutôt qu'un seul, bien que les deux migrations soient générées automatiquement.

cette procédure fonctionne sur Django 1.7+.

1
répondu David Foster 2015-12-22 23:07:21