web-dev-qa-db-fra.com

Le moyen le plus simple de renommer un modèle avec Django / South?

J'ai cherché une réponse à cela sur le site de South, Google et SO, mais je n'ai pas trouvé de moyen simple de le faire.

Je veux renommer un modèle Django utilisant South. Dites que vous avez ce qui suit:

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

class FooTwo(models.Model):
    name = models.CharField()
    foo = models.ForeignKey(Foo)

et vous voulez convertir Foo en Bar, à savoir

class Bar(models.Model):
    name = models.CharField()

class FooTwo(models.Model):
    name = models.CharField()
    foo = models.ForeignKey(Bar)

Pour faire simple, j'essaie simplement de changer le nom de Foo en Bar, mais j'ignore le membre foo dans FooTwo pour l'instant.

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

  1. Je pourrais probablement faire une migration de données, mais cela semble assez compliqué.
  2. Écrivez une migration personnalisée, par exemple db.rename_table('city_citystate', 'geo_citystate'), mais je ne sais pas comment réparer la clé étrangère dans ce cas.
  3. Une manière plus simple que vous connaissez?
141
vaughnkoch

Pour répondre à votre première question, le simple changement de nom de modèle/table est assez simple. Exécutez la commande:

./manage.py schemamigration yourapp rename_foo_to_bar --empty

(Mise à jour 2: essayez --auto au lieu de --empty pour éviter l'avertissement ci-dessous. Merci à @KFB pour l'astuce.)

Si vous utilisez une ancienne version de south, vous aurez besoin de startmigration au lieu de schemamigration.

Modifiez ensuite manuellement le fichier de migration pour qu'il ressemble à ceci:

class Migration(SchemaMigration):

    def forwards(self, orm):
        db.rename_table('yourapp_foo', 'yourapp_bar')


    def backwards(self, orm):
        db.rename_table('yourapp_bar','yourapp_foo')   

Vous pouvez accomplir cela plus simplement en utilisant le db_table Option Meta dans votre classe de modèle. Mais chaque fois que vous faites cela, vous augmentez le poids hérité de votre base de code - le fait que les noms de classe diffèrent des noms de table rend votre code plus difficile à comprendre et à maintenir. Je soutiens pleinement la refonte simple comme celle-ci dans un souci de clarté.

(mise à jour) Je viens de l'essayer en production et j'ai reçu un étrange avertissement lorsque je suis allé appliquer la migration. Ça disait:

The following content types are stale and need to be deleted:

    yourapp | foo

Any objects related to these content types by a foreign key will also
be deleted. Are you sure you want to delete these content types?
If you're unsure, answer 'no'.

J'ai répondu "non" et tout semblait aller bien.

130
Leopd

Apportez les modifications dans models.py puis exécutez

./manage.py schemamigration --auto myapp

Lorsque vous inspectez le fichier de migration, vous verrez qu'il supprime une table et en crée une nouvelle

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Deleting model 'Foo'                                                                                                                      
        db.delete_table('myapp_foo')

        # Adding model 'Bar'                                                                                                                        
        db.create_table('myapp_bar', (
        ...
        ))
        db.send_create_signal('myapp', ['Bar'])

    def backwards(self, orm):
        ...

Ce n'est pas tout à fait ce que vous voulez. Modifiez plutôt la migration pour qu'elle ressemble à:

class Migration(SchemaMigration):

    def forwards(self, orm):
        # Renaming model from 'Foo' to 'Bar'                                                                                                                      
        db.rename_table('myapp_foo', 'myapp_bar')                                                                                                                        
        if not db.dry_run:
            orm['contenttypes.contenttype'].objects.filter(
                app_label='myapp', model='foo').update(model='bar')

    def backwards(self, orm):
        # Renaming model from 'Bar' to 'Foo'                                                                                                                      
        db.rename_table('myapp_bar', 'myapp_foo')                                                                                                                        
        if not db.dry_run:
            orm['contenttypes.contenttype'].objects.filter(app_label='myapp', model='bar').update(model='foo')

En l'absence de l'instruction update, le db.send_create_signal l'appel créera un nouveau ContentType avec le nouveau nom de modèle. Mais il est préférable de simplement update le ContentType que vous avez déjà au cas où des objets de base de données pointent vers lui (par exemple, via un GenericForeignKey).

De plus, si vous avez renommé certaines colonnes qui sont des clés étrangères au modèle renommé, n'oubliez pas de

db.rename_column(myapp_model, foo_id, bar_id)
66
Jian

Le Sud ne peut pas le faire lui-même - comment sait-il que Bar représente ce que Foo avait l'habitude de faire? C'est le genre de chose pour laquelle j'écrirais une migration personnalisée. Vous pouvez modifier votre ForeignKey dans le code comme vous l'avez fait ci-dessus, puis il s'agit simplement de renommer les champs et les tables appropriés, que vous pouvez faire comme vous le souhaitez.

Enfin, avez-vous vraiment besoin de faire cela? Je n'ai pas encore besoin de renommer les modèles - les noms de modèle ne sont qu'un détail d'implémentation - en particulier compte tenu de la disponibilité de verbose_name Option méta.

5
Dominic Rodger