Django - Wie benenne ich ein Modellfeld mit South um?

209

Ich möchte einen Namen für bestimmte Felder in einem Modell ändern:

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

sollte sich ändern zu:

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

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

Jonathan
quelle
7
Siehe auch stackoverflow.com/questions/2862979/... ein für die Umbenennung Modell eher als ein Modellfeld .
Mechanische Schnecke

Antworten:

230

Sie können die db.rename_columnFunktion verwenden.

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

Das erste Argument von db.rename_columnist der Tabellenname. Daher ist es wichtig, sich daran zu erinnern, wie Django Tabellennamen erstellt :

Django leitet den Namen der Datenbanktabelle automatisch aus dem Namen Ihrer Modellklasse und der App ab, die sie enthält. Der Name der Datenbanktabelle eines Modells wird erstellt, indem das "App-Label" des Modells - der Name, den Sie in manage.py startapp verwendet haben - mit dem Klassennamen des Modells verbunden wird, wobei ein Unterstrich dazwischen steht.

In dem Fall, in dem Sie einen mehrwortigen Modellnamen mit Kamelgehäuse haben, wie z. B. ProjectItem, lautet der Tabellenname app_projectitem(dh es wird kein Unterstrich zwischen projectund mit itemKamelgehäuse eingefügt ).

googletorp
quelle
2
Das würde für name \ full_name funktionieren, aber nicht für das Beziehungsfeld, oder?
Jonathan
23
WICHTIGER HINWEIS: Wenn Sie dies verwenden möchten, stellen Sie sicher, dass "app_foo" der Name der Datenbanktabelle ist. Beispiel: "mainapp_profile", "name" ist der alte Spaltenname der Datenbank (nicht der Name des Modellfelds). Beispiel: "Benutzer-ID" und "vollständiger Name" sind der neue Name, den die Spalte haben soll (wiederum Datenbankspalte und nicht Feldname). Also: db.rename_column ('mainapp_profile', 'user_id', 'new_user_id') Wenn Sie mit Fremdschlüsseln arbeiten, sollten Sie beim Umbenennen den Teil "_id" in ihnen haben.
Gezim
3
Wenn Sie diese db.rename_column manuell ausführen, müssen Sie möglicherweise anschließend eine gefälschte Schemamigration durchführen, um die Daten zu bereinigen. Das heißt, migrieren Sie zuerst die Änderung mit dem Umbenennen der Spalten. Korrigieren Sie dann das Modell (um den aktualisierten Namen zu erhalten) und führen Sie dann die Schema-Migrations-App .auto && ./manage.py migrate app --fake aus ./manage.py.
Dr. Jimbob
24
Sie können auch ./manage.py schemamigration my_app renaming_column_x verwenden - leer, um eine leere Migration zu erstellen und nur den Code darin zu platzieren
Ilian Iliev
11
--empty hilft nicht viel, verwenden Sie stattdessen --auto und ändern Sie die erstellte Migration. Auf diese Weise wird nicht behauptet, dass das Feld für die nächste Migration von models.py gelöscht wurde.
Yasc
39

Folgendes mache ich:

  1. Nehmen Sie den Spaltennamen in Ihrem Modell vor (in diesem Beispiel wäre dies der Fall myapp/models.py).
  2. Lauf ./manage.py schemamigration myapp renaming_column_x --auto

Hinweis renaming_column_x kann beliebig sein. Es handelt sich lediglich um eine Möglichkeit, der Migrationsdatei einen beschreibenden Namen zu geben.

Dadurch wird eine Datei mit dem Namen generiert myapp/migrations/000x_renaming_column_x.py die Ihre alte Spalte löscht und eine neue Spalte hinzufügt.

Ändern Sie den Code in dieser Datei, um das Migrationsverhalten in eine einfache Umbenennung zu ändern:

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
quelle
Wie heißt die Spalte in Ihrem Befehl? xoder column_x?
Andilabs
Der Teil des Befehls, auf den Sie sich beziehen, wird nur zum Benennen der Migration verwendet. Er kann beliebig sein. Der Spaltenname muss beim Bearbeiten in der Migrationsdatei angegeben werden.
Donturner
2
Das --autoerste Erstellen der Migration ist ein guter Tipp. Es vermeidet Probleme mit dem Süden ORM Freezer, der nur dann , wenn die Migration auftreten hat forwardsund backwardsMethoden, aber enthält nicht das gefrorene modelObjekt.
MWCZ
Wie beantworte ich die Frage von South "Da Sie dieses Feld entfernen, MÜSSEN Sie einen Standard angeben"?
Bryce
3
Das einzige Problem, das ich mit dieser Methode habe, ist, dass db.rename_columndie mit der Spalte verbundenen Einschränkungen nicht umbenannt werden . Die Migration funktioniert weiterhin, aber Sie haben Einschränkungen, die nach dem alten Spaltennamen benannt sind. Ich hatte eine Spalte mit einer Eindeutigkeitsbeschränkung, benannte sie mit dieser Methode um, testete, dass die Eindeutigkeitsbeschränkung noch vorhanden war, und bekam einen Fehler, aber der Name der Einschränkung selbst verwendete immer noch den alten Spaltennamen. Vielleicht eine explizite db.delete_uniqueund db.create_uniquehätte es getan, aber ich entschied mich für sjhs Lösung.
Louis
15

Ich wusste nichts über die Spalte db.rename, klingt praktisch, aber in der Vergangenheit habe ich die neue Spalte als eine Schemamigration hinzugefügt, dann eine Datenmigration erstellt, um Werte in das neue Feld zu verschieben, und dann eine zweite Schemamigration, um die alte Spalte zu entfernen

sjh
quelle
Ich auch. Das Problem bei der Schema-Daten-Schema-Technik besteht darin, dass Sie unterschiedliche Namen für Ihre Felder / Modelle erhalten. Manchmal ist dies ein Problem, wenn Sie kanonische Namen verwenden, um Dispatcher zu aktivieren ...
Jonathan
Ich habe es gerade versucht und es hat nicht funktioniert, da es einen Namenskonflikt gab. Ich hatte genau den gleichen FK, aber einen anderen Namen. Es wurde nicht validiert. Also tu das nicht .
Gezim
2
@ Jonathan, er will verschiedene Namen! ... @ Pilgrim, möchtest du einen Code posten? Ich habe dies diese Woche mehrmals getan. Wenn Ihre Modelle nicht validiert werden, erstellt South die Migrationen nicht.
sjh
Das würde funktionieren, wäre aber wahrscheinlich langsamer als die 'rename_column', die andere Leute vorgeschlagen haben. Ich weiß, dass MySQL ziemlich schnell eine "ALTER TABLE ... Rename Column" (oder was auch immer es ist) erstellen kann.
Rory
1
@Rory db.rename_columnbenennt Einschränkungen nicht für Sie um, daher müssen Sie diese manuell behandeln. Wenn Sie dies vergessen , funktioniert die Migration mit der Ausnahme, dass ohne Ihr Wissen möglicherweise noch eine Einschränkung mit dem alten Spaltennamen vorliegt. Es ist mir unklar, ob das Problem nur kosmetischer Natur ist oder ob es in einer zukünftigen Migration, in der die Einschränkung manipuliert oder nach Süden fallen gelassen werden sollte, nicht in der Lage sein wird, es zu finden. Auf jeden Fall ist es der sichere Weg, dies hier zu tun, wie es sjh vorschlägt: Sie können South herausfinden lassen, was es herausfinden sollte.
Louis
10

Mit Django 1.7 wurden Migrationen eingeführt, sodass Sie jetzt nicht einmal mehr ein zusätzliches Paket installieren müssen, um Ihre Migrationen zu verwalten.

Um Ihr Modell umzubenennen, müssen Sie zuerst eine leere Migration erstellen:

$ manage.py makemigrations <app_name> --empty

Dann müssen Sie den Code Ihrer Migration wie folgt bearbeiten:

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

Und danach müssen Sie laufen:

$ manage.py migrate <app_name>
Dmitrii Mikhailov
quelle
5

Ändern Sie einfach das Modell und führen Sie makemigrations1.9 aus

Django erkennt automatisch, dass Sie ein einzelnes Feld gelöscht und erstellt haben, und fragt:

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

Sagen Sie "Ja" und die richtige Migration wird erstellt. Magie.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
0
  1. Fügen Sie southIhren installierten Apps in der Projekteinstellungsdatei hinzu.
  2. Kommentieren Sie das hinzugefügte / geänderte Feld / die Tabelle aus.
  3. $ manage.py Schemamigration <app_name> --initial
  4. $ manage.py migrate <app_name> --Fake
  5. Kommentieren Sie das Feld aus und schreiben Sie das geänderte
  6. $ manage.py Schemamigration --auto
  7. $ manage.py migrate <app_name>

Wenn Sie 'pycharm' verwenden, können Sie 'ctrl + shift + r' anstelle von 'manage.py' und 'shift' für Parameter verwenden.

Ancho
quelle