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?
db.rename_table('city_citystate', 'geo_citystate')
, mais je ne sais pas comment réparer la clé étrangère dans ce cas.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.
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)
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.