Der einfachste Weg, ein Modell mit Django / South umzubenennen?

141

Ich habe auf der Website von South, Google und SO nach einer Antwort darauf gesucht, konnte aber keinen einfachen Weg finden, dies zu tun.

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

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

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

und Sie möchten nämlich Foo in Bar konvertieren

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

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

Um es einfach zu halten, versuche ich nur, den Namen von Fooin zu ändern Bar, aber ignoriere das fooMitglied FooTwovorerst.

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

  1. Ich könnte wahrscheinlich eine Datenmigration durchführen, aber das scheint ziemlich kompliziert zu sein.
  2. Schreiben Sie beispielsweise eine benutzerdefinierte Migration, db.rename_table('city_citystate', 'geo_citystate')aber ich bin nicht sicher, wie der Fremdschlüssel in diesem Fall behoben werden soll.
  3. Ein einfacher Weg, den Sie kennen?
vaughnkoch
quelle
5
Siehe auch stackoverflow.com/questions/3235995/... für ein Umbenennung Modellfeld eher als ein Modell .
Mechanische Schnecke
Optimierte Lösung für Django> = 1.8 stackoverflow.com/questions/25091130/…
Chemischer Programmierer

Antworten:

130

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

./manage.py schemamigration yourapp rename_foo_to_bar --empty

(Update 2: Versuchen Sie --autostattdessen --empty, die folgende Warnung zu vermeiden. Vielen Dank an @KFB für den Tipp.)

Wenn Sie eine ältere Version von South verwenden, benötigen Sie startmigrationstattdessen schemamigration.

Bearbeiten Sie dann die Migrationsdatei manuell wie folgt:

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

Sie können dies einfacher mit der db_tableOption Meta in Ihrer Modellklasse erreichen. Aber jedes Mal, wenn Sie dies tun, erhöhen Sie das Legacy-Gewicht Ihrer Codebasis. Wenn sich Klassennamen von Tabellennamen unterscheiden, ist Ihr Code schwieriger zu verstehen und zu pflegen. Der Klarheit halber unterstütze ich die Durchführung einfacher Refactorings wie diese voll und ganz.

(Update) Ich habe dies gerade in der Produktion versucht und eine seltsame Warnung erhalten, als ich die Migration anwendete. 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.

Leopd
quelle
3
Ich konnte die Fehlermeldung von Leopd vermeiden, indem ich die Schemamigration mit --auto anstelle von --empty erstellte. Ich habe dann die Migrationsdatei bearbeitet und das Löschen / Erstellen der Tabellen in einen Aufruf von db.rename_table () geändert. Dies scheint sehr gut funktioniert zu haben.
KFB
4
Ich habe diese Technik am 02.09.2011 ohne Fehler angewendet. Vielleicht hat eine neuere Version von South das Problem mit den Fehlern gelöst.
Chip Tol
1
Vielen Dank, dass Sie dies auf dem neuesten Stand halten! Jians Antwort unten besagt, dass es wichtig ist, die "send_create_signal" -Aufrufe beizubehalten. Haben Sie irgendwelche Kenntnisse darüber? Wenn Sie damit einverstanden sind, ist es hilfreich, Ihre Beispielmigration zu aktualisieren.
Mrooney
5
Beachten Sie, dass dadurch keine Indizes für diese Tabelle umbenannt werden. Wenn Sie in Zukunft eine neue Tabelle mit demselben Namen wie die alte Tabelle erstellen, können Fehler auftreten, wenn Indexnamen kollidieren. Wir haben diese Technik verwendet, aber von nun an werden wir die neue Tabelle explizit erstellen, die Daten migrieren und dann die alte Tabelle löschen.
Jeremy Banks
3
Spaltennamen in automatisch generierten Tabellen, z. B. M2M-Tabellen, zum ursprünglichen Modell werden von dieser Methode ebenfalls nicht migriert.
Spookylukey
66

Nehmen Sie die Änderungen vor models.pyund führen Sie sie aus

./manage.py schemamigration --auto myapp

Wenn Sie die Migrationsdatei untersuchen, werden Sie feststellen, dass eine Tabelle gelöscht und eine neue erstellt wird

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

Das ist nicht ganz das, was Sie wollen. Bearbeiten Sie stattdessen die Migration so, dass sie 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')

Wenn die updateAnweisung nicht vorhanden ist, wird beim db.send_create_signalAufruf ein neuer ContentTypemit dem neuen Modellnamen erstellt. Es ist jedoch besser, nur updatedas zu haben, was ContentTypeSie bereits haben, falls Datenbankobjekte darauf verweisen (z. B. über a GenericForeignKey).

Wenn Sie einige Spalten umbenannt haben, die Fremdschlüssel für das umbenannte Modell sind, vergessen Sie dies nicht

db.rename_column(myapp_model, foo_id, bar_id)
Jian
quelle
2
Ich erhalte die Fehlermeldung KeyError: "Das Modell 'contenttype' aus der App 'contenttypes' ist in dieser Migration nicht verfügbar." Außerdem habe ich eine Tabelle "django_content_type", aber keine Tabelle "contenttypes". (Django 1.6)
Seth
2
@ Seth Ich habe dies umgangen, indem ich die ContentType-Modelle in einer separaten Datenmigration aktualisiert und das contenttypes.ContentTypeModell mithilfe des --frozenFlags zu den eingefrorenen Modellen hinzugefügt habe ./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 Code zur Aktualisierung des Inhaltstyps.
Geoffrey Hing
@GeoffreyHing Ich denke, der Parameter ist Freeze No Frozen. south.readthedocs.io/en/latest/ormfreezing.html Aber vielen Dank für Ihre Hilfe, es war wirklich hilfreich.
ccsakuweb
5

Der Süden kann es nicht selbst tun - woher weiß er, dass dies das Bardarstellt, was Foofrüher war? Für diese Art von Dingen würde ich eine benutzerdefinierte Migration schreiben. Sie können Ihren ForeignKeyIn-Code wie oben beschrieben ändern. Anschließend müssen Sie nur die entsprechenden Felder und Tabellen umbenennen, was Sie nach Belieben tun können.

Müssen Sie das wirklich tun? Ich muss Modelle noch umbenennen - Modellnamen sind nur ein Implementierungsdetail - insbesondere angesichts der Verfügbarkeit der verbose_nameMeta-Option.

Dominic Rodger
quelle
7
Alternativ können Sie das Modell im Code umbenennen, aber die db_tableOption Meta verwenden, um den Namen der Datenbanktabelle beizubehalten.
Daniel Roseman
@ Daniel - wissen Sie, ob db_tableFremdschlüsselnamen abgeleitet werden?
Dominic Rodger
Ich glaube es ist. Wenn Sie den Modellnamen ändern und db_table festlegen, sollte alles wie erwartet funktionieren.
Davor Lucic
1
@ DanielRoseman das ist die beste Lösung im ganzen Thread!
Joerick
-1

Ich folgte der obigen Lösung von Leopd. Dies änderte jedoch nichts an den Modellnamen. Ich habe es manuell im Code geändert (auch in verwandten Modellen, in denen dies als FK bezeichnet wird). Und eine weitere Südwanderung gemacht, aber mit --fake Option. Dadurch werden Modell- und Tabellennamen identisch.

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

Gowthaman
quelle