2010-05-19 7 views
138

Ich habe auf der South-Website, Google und SO nach einer Antwort gesucht, konnte aber keinen einfachen Weg finden, dies zu tun.Einfachste Möglichkeit, ein Modell mit Django/South umzubenennen?

Ich möchte ein Django-Modell mit South umbenennen. Angenommen, Sie haben die folgenden:

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

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

und Sie möchten Foo konvertieren, es einfach zu Bar, nämlich

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

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

zu halten, ich bin gerade von Foo zu Bar den Namen zu ändern versuchen, aber ignorieren Sie das foo Mitglied in FooTwo für jetzt.

Was ist der einfachste Weg, dies mit South zu tun?

  1. Ich könnte wahrscheinlich eine Datenmigration tun, aber das scheint ziemlich beteiligt.
  2. Schreiben Sie eine benutzerdefinierte Migration, z. db.rename_table('city_citystate', 'geo_citystate'), aber ich bin mir nicht sicher, wie man den Fremdschlüssel in diesem Fall repariert.
  3. Ein einfacher Weg, den Sie kennen?
+4

Siehe auch http://stackoverflow.com/questions/3235995/django-how-to-rename-a-model-field-using-south zum Umbenennen eines * Modellfelds * anstelle eines * Modells *. –

+0

Optimierte Lösung für Django> = 1.8 http://stackoverflow.com/questions/25091130/django-migration-strategy-for-renaming-a-model-and-relationshipfields –

Antwort

128

Um Ihre erste Frage zu beantworten, ist das einfache Modell/Tabelle umbenennen ziemlich einfach. Führen Sie den Befehl:

./manage.py schemamigration yourapp rename_foo_to_bar --empty 

(Update 2:.. Versuchen --auto statt --empty die Warnung unten zu vermeiden, Danke für den Tipp @KFB)

Wenn Sie eine ältere Version von Süden verwenden, Sie benötigen startmigration anstelle von schemamigration.

bearbeiten dann manuell die Migrationsdatei wie folgt aussehen:

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

du einfach mehr erreichen können, die db_table Meta-Option in Ihrem Modell-Klasse. Aber jedes Mal, wenn Sie das tun, erhöhen Sie das Legacy-Gewicht Ihrer Codebasis. Wenn Sie Klassennamen von Tabellennamen unterscheiden, wird Ihr Code schwieriger zu verstehen und zu pflegen. Ich unterstütze solche einfachen Refactorings aus Gründen der Übersichtlichkeit.

(update) Ich habe gerade dies in der Produktion versucht, und bekam eine seltsame Warnung, als ich ging, um die Migration anzuwenden. Es sagte:

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'. 

Ich antwortete "Nein" und alles schien in Ordnung zu sein.

+0

Wenn ich das mache, verliere ich alle Zeilen in foo. Was mache ich falsch? – jMyles

+0

@Justin - schwer zu sagen, ohne viel mehr Details. Schreiben Sie eine neue SO Frage und verlinken Sie sie hier in einem Kommentar? – Leopd

+0

Dies scheint alte Migrationen zu brechen, die von 'Foo' abhingen. – luqui

5

Süden kann es nicht selbst tun - woher weiß es, dass Bar darstellt, was Foo verwendet? Das ist die Art von Dingen, für die ich eine benutzerdefinierte Migration schreiben würde. Sie können Ihre ForeignKey in Code ändern, wie Sie oben getan haben, und dann ist es nur ein Fall der Umbenennung der entsprechenden Felder und Tabellen, die Sie tun können, wie Sie wollen.

Schließlich, müssen Sie das wirklich tun? Ich muss noch Modelle umbenennen - Modellnamen sind nur ein Implementierungsdetail - besonders angesichts der Verfügbarkeit der Meta-Option verbose_name.

+7

Alternativ können Sie das Modell im Code umbenennen, aber verwenden Sie die 'db_table' Meta-Option, um den Namen der Datenbanktabelle beizubehalten. –

+0

@Daniel - Wissen Sie, ob 'db_table' zum Ableiten von Fremdschlüsselnamen verwendet wird? –

+0

ich glaube es ist. Wenn Sie den Modellnamen ändern und db_table setzen, sollte alles wie erwartet funktionieren. –

65

die Änderungen in models.py Stellen und dann

./manage.py schemamigration --auto myapp 

laufen Wenn Sie die Migrationsdatei überprüfen, werden Sie sehen, dass es eine Tabelle löscht und erstellt ein neues

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): 
     ... 

Dies ist nicht ganz was du willst. Stattdessen die Migration bearbeiten, so dass es wie folgt aussieht:

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

In Abwesenheit der Anweisung update, der db.send_create_signal Aufruf wird eine neues ContentType mit dem neuen Modellnamen erstellen. Aber es ist besser, nur update die ContentType Sie haben bereits im Falle, dass Datenbankobjekte darauf zeigen (z. B. über eine GenericForeignKey).

Auch, wenn Sie einige Spalten umbenannt haben, die Fremdschlüssel zu dem umbenannten Modell sind, vergessen Sie nicht zu

db.rename_column(myapp_model, foo_id, bar_id) 
+8

+1 zum Umbenennen des Modells in der Tabelle contenttypes – ACGray

+2

Ich erhalte den Fehler KeyError: "Das Modell 'contenttype' aus der App 'contenttypes' ist in dieser Migration nicht verfügbar. " Außerdem habe ich eine django_content_type-Tabelle, aber keine contenttypes-Tabelle. (Django 1.6) – Seth

+2

@Seth Ich habe daran gearbeitet, indem ich die ContentType-Modelle in einer separaten Datenmigration aktualisiert habe und das Modell "contentypes.ContentType" den eingefrorenen Modellen mit dem '' -frozen'' hinzugefügt habe Markieren Sie './manage.py datamigration''. Zum Beispiel: '' ./manage.py datamigration --Frozen contenttypes myapp update_contenttypes''. Bearbeiten Sie dann myapp_migrations/NNNN_update_contenttypes.py mit dem oben angegebenen Aktualisierungscode für den Inhaltstyp. –

-1

I Leopd Lösung oben gefolgt. Aber das änderte die Modellnamen nicht. Ich habe es manuell im Code geändert (auch in verwandten Modellen, wo dies als FK bezeichnet wird). Und noch eine Südwanderung, aber mit --fake Option. Dies macht Modellnamen und Tabellennamen gleich.

Nur realisiert, man könnte zuerst mit dem Ändern von Modellnamen beginnen und dann die Migrationsdatei bearbeiten, bevor man sie anwendet. Viel sauberer.