Django - Comment renommer un champ modèle en utilisant South?

209

Je voudrais changer le nom de champs spécifiques dans un modèle:

class Foo(models.Model):
    name = models.CharField()
    rel  = models.ForeignKey(Bar)

devrait changer en:

class Foo(models.Model):
    full_name     = models.CharField()
    odd_relation  = models.ForeignKey(Bar)

Quelle est la façon la plus simple de le faire en utilisant South?

Jonathan
la source
7
Voir aussi stackoverflow.com/questions/2862979/… pour renommer un modèle plutôt qu'un champ de modèle .
Escargot mécanique

Réponses:

230

Vous pouvez utiliser la db.rename_columnfonction.

class Migration:

    def forwards(self, orm):
        # Rename 'name' field to 'full_name'
        db.rename_column('app_foo', 'name', 'full_name')




    def backwards(self, orm):
        # Rename 'full_name' field to 'name'
        db.rename_column('app_foo', 'full_name', 'name')

Le premier argument de db.rename_columnest le nom de la table, il est donc important de se rappeler comment Django crée les noms de table :

Django dérive automatiquement le nom de la table de base de données du nom de votre classe de modèle et de l'application qui la contient. Le nom de la table de base de données d'un modèle est construit en joignant le "label d'application" du modèle - le nom que vous avez utilisé dans manage.py startapp - au nom de classe du modèle, avec un trait de soulignement entre eux.

Dans le cas où vous avez un nom de modèle à plusieurs mots et à boîtier chameau, tel que ProjectItem, le nom de la table sera app_projectitem(c'est-à-dire qu'un trait de soulignement ne sera pas inséré entre projectet itemmême s'ils sont à boîtier chameau).

googletorp
la source
2
Cela fonctionnerait sur le nom \ nom complet, mais pas sur le champ relation, non?
Jonathan
23
REMARQUE IMPORTANTE: si vous comptez l'utiliser, assurez-vous que "app_foo" est le nom de la table de base de données, par exemple: "mainapp_profile", "name" est l'ancien nom de colonne de la base de données (pas le nom du champ de modèle), par exemple: "user_id" et "full_name" serait le nouveau nom que vous voulez que la colonne ait (encore une fois, la colonne de la base de données et non le nom du champ). Donc: db.rename_column ('mainapp_profile', 'user_id', 'new_user_id') De plus, si vous avez affaire à des clés étrangères, vous devriez avoir la partie "_id" en elles lors du changement de nom.
Gezim
3
Si vous effectuez manuellement cette db.rename_column, vous devrez peut-être effectuer une fausse migration de schéma par la suite pour nettoyer les choses. C'est d'abord migrer la modification en renommant les colonnes. Ensuite, corrigez le modèle (pour avoir le nom mis à jour), puis effectuez l'application de schemamigration ./manage.py --auto && ./manage.py migrate app --fake).
dr jimbob
24
Vous pouvez également utiliser ./manage.py schemamigration my_app renaming_column_x --empty pour créer une migration vide et juste pour y placer le code
Ilian Iliev
11
--empty n'aide pas beaucoup, utilisez plutôt --auto et modifiez la migration créée. De cette façon, il ne prétendra pas que le champ a été supprimé pour la prochaine migration de models.py.
yasc
39

Voici ce que je fais:

  1. Modifiez le nom de la colonne dans votre modèle (dans cet exemple, ce serait myapp/models.py)
  2. Courir ./manage.py schemamigration myapp renaming_column_x --auto

La note renaming_column_xpeut être tout ce que vous aimez, c'est juste un moyen de donner un nom descriptif au fichier de migration.

Cela vous générera un fichier appelé myapp/migrations/000x_renaming_column_x.pyqui supprimera votre ancienne colonne et ajoutera une nouvelle colonne.

Modifiez le code de ce fichier pour changer le comportement de migration en un simple renommage:

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Renaming column 'mymodel.old_column_name' to 'mymodel.new_column_name'
        db.rename_column(u'myapp_mymodel', 'old_column_name', 'new_column_name')

    def backwards(self, orm):
        # Renaming column 'mymodel.new_column_name' to 'mymodel.old_column_name'
        db.rename_column(u'myapp_mymodel', 'new_column_name', 'old_column_name')
donturner
la source
quel est le nom de la colonne dans votre commande? xou column_x?
andilabs
La partie de la commande à laquelle vous faites référence est juste utilisée pour nommer la migration, elle peut être ce que vous voulez. Le nom de la colonne devra être spécifié dans le fichier de migration lorsque vous le modifiez.
donturner
2
La création de la --automigration en premier est un excellent conseil. Cela évite les problèmes avec le congélateur ORM Sud, qui se produisent si la migration ne comporte forwardsque des backwardsméthodes et ne contient pas l' modelobjet figé .
mwcz
Comment puis-je répondre à la question du sud «Puisque vous supprimez ce champ, vous DEVEZ spécifier une valeur par défaut»?
Bryce
3
Le seul problème que j'ai avec cette méthode est qu'elle db.rename_columnne renomme pas les contraintes associées à la colonne. La migration fonctionnera toujours mais vous aurez des contraintes nommées d'après l'ancien nom de colonne. J'ai eu une colonne avec une contrainte d'unicité, je l'ai renommée à l'aide de cette méthode, j'ai testé que la contrainte d'unicité existait toujours et j'ai eu une erreur mais le nom de la contrainte lui-même utilisait toujours l'ancien nom de colonne. Peut-être une explicite db.delete_uniqueet l' db.create_uniqueaurait fait mais j'ai décidé d'aller avec la solution de sjh.
Louis
15

Je ne connaissais pas la colonne db.rename, cela semble pratique, mais dans le passé, j'ai ajouté la nouvelle colonne comme une schémamigration, puis créé une migration de données pour déplacer les valeurs dans le nouveau champ, puis une deuxième schémamigration pour supprimer l'ancienne colonne

sjh
la source
Moi aussi. Le problème avec la technique schema-data-schema est que vous vous retrouvez avec des noms différents pour vos champs / modèles. Parfois, c'est un problème si vous utilisez des noms canoniques pour activer les répartiteurs ...
Jonathan
J'ai juste essayé ceci et cela n'a pas fonctionné car il y avait un conflit de nom. J'avais exactement le même FK mais un nom différent. Cela n'a pas validé. Alors ne fais pas ça.
Gezim
2
@jonathan, il veut des noms différents! ... @pilgrim, tu veux poster du code? Je l'ai fait plusieurs fois cette semaine, si vos modèles ne valident pas, le sud ne créera pas les migrations.
sjh
Cela fonctionnerait, mais serait probablement plus lent que le 'rename_column' que d'autres personnes ont suggéré. Je sais que MySQL peut faire une "ALTER TABLE ... renommer la colonne" (ou quoi que ce soit) assez rapidement.
Rory
1
@Rory db.rename_columnne renommera pas les contraintes pour vous, vous devez donc gérer cela manuellement. Si vous oubliez de le faire, la migration fonctionnera sauf que, à votre insu, il peut y avoir une contrainte utilisant toujours l'ancien nom de colonne. Il n'est pas clair pour moi si le problème est purement cosmétique ou si dans une future migration où la contrainte devrait être manipulée ou abandonnée au Sud ne pourra pas la trouver. En tout cas, le faire ici comme le suggère sjh est le moyen le plus sûr de le faire: vous pouvez laisser South comprendre ce qu'il devrait comprendre.
Louis
10

Django 1.7 a introduit les migrations , vous n'avez donc plus besoin d'installer de package supplémentaire pour gérer vos migrations.

Pour renommer votre modèle, vous devez d'abord créer une migration vide:

$ manage.py makemigrations <app_name> --empty

Ensuite, vous devez modifier le code de votre migration comme ceci:

from django.db import models, migrations

class Migration(migrations.Migration):

dependencies = [
    ('yourapp', 'XXXX_your_previous_migration'),
]

operations = [
    migrations.RenameField(
        model_name='Foo',
        old_name='name',
        new_name='full_name'
    ),
    migrations.RenameField(
        model_name='Foo',
        old_name='rel',
        new_name='odd_relation'
    ),
]

Et après cela, vous devez exécuter:

$ manage.py migrate <app_name>
Dmitrii Mikhailov
la source
5

Il suffit de changer de modèle et de lancer la version makemigrations1.9

Django détecte automatiquement que vous avez supprimé et créé un seul champ et demande:

Did you rename model.old to model.new (a IntegerField)? [y/N]

Dites oui et la bonne migration est créée. La magie.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
0
  1. Ajoutez southà vos applications installées dans le fichier de paramètres du projet.
  2. Mettez en commentaire le champ / tableau ajouté / modifié.
  3. $ manage.py Schemamigration <app_name> --initial
  4. $ manage.py migrate <app_name> --Fake
  5. Décommentez le champ et écrivez le champ modifié
  6. $ manage.py Schemamigration --auto
  7. $ manage.py migrate <app_name>

Si vous utilisez 'pycharm', vous pouvez utiliser 'ctrl + shift + r' au lieu de 'manage.py' et 'shift' pour les paramètres.

ancho
la source