Django - Come rinominare un campo modello usando South?


209

Vorrei cambiare un nome di campi specifici in un modello:

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

dovrebbe cambiare in:

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

Qual è il modo più semplice per farlo usando South?


7
Vedere anche stackoverflow.com/questions/2862979/… per rinominare un modello anziché un campo modello .
Lumaca meccanica,

Risposte:


230

Puoi usare la db.rename_columnfunzione.

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

Il primo argomento di db.rename_columnè il nome della tabella, quindi è importante ricordare come Django crea i nomi delle tabelle :

Django deriva automaticamente il nome della tabella del database dal nome della classe del modello e dall'app che lo contiene. Il nome della tabella del database di un modello viene creato unendo la "etichetta dell'app" del modello - il nome che hai usato in manage.py startapp - al nome della classe del modello, con un carattere di sottolineatura tra di loro.

Nel caso in cui si disponga di un nome di modello a più parole, incastonato in un cammello, come ProjectItem, il nome della tabella sarà app_projectitem(ovvero, un carattere di sottolineatura non verrà inserito tra projecte itemanche se in un caso di cammello).


2
Funzionerebbe su name \ full_name, ma non sul campo delle relazioni, giusto?
Jonathan,

23
NOTA IMPORTANTE: se si intende utilizzare questo, assicurarsi che "app_foo" sia il nome della tabella del database, quindi ad esempio: "mainapp_profile", "name" è il vecchio nome della colonna del database (non il nome del campo del modello), ad esempio: "user_id" e "full_name" sarebbe il nuovo nome che si desidera che la colonna abbia (di nuovo, colonna del database e non nome del campo). Quindi: db.rename_column ('mainapp_profile', 'user_id', 'new_user_id') Inoltre, se hai a che fare con chiavi esterne, dovresti avere la parte "_id" in esse durante la ridenominazione.
Gezim,

3
Se lo fai manualmente db.rename_column potresti dover fare uno schema di migrazione falso in seguito per ripulire le cose. Ciò significa innanzitutto migrare la modifica con la ridenominazione delle colonne. Quindi correggi il modello (per avere il nome aggiornato), quindi esegui ./manage.py schemamigration app --auto && ./manage.py migrate app --fake).
dr jimbob,

24
Puoi anche utilizzare ./manage.py schemamigration my_app renaming_column_x --empty per creare una migrazione vuota e solo per inserire il codice al suo interno
Ilian Iliev

11
--empty non aiuta molto, invece usa --auto e modifica la migrazione creata. In questo modo il campo non verrà rivendicato è stato eliminato per la prossima migrazione di models.py.
yasc,

39

Ecco cosa faccio:

  1. Apporta la modifica del nome della colonna nel tuo modello (in questo esempio sarebbe myapp/models.py)
  2. Correre ./manage.py schemamigration myapp renaming_column_x --auto

La nota renaming_column_xpuò essere qualsiasi cosa ti piaccia, è solo un modo per dare un nome descrittivo al file di migrazione.

Questo genererà un file chiamato myapp/migrations/000x_renaming_column_x.pyche eliminerà la tua vecchia colonna e aggiungerà una nuova colonna.

Modifica il codice in questo file per cambiare il comportamento della migrazione in una semplice ridenominazione:

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

qual è il nome della colonna nel tuo comando? xo column_x?
andilabs

La parte del comando a cui ti riferisci viene utilizzata solo per denominare la migrazione, può essere come preferisci. Il nome della colonna dovrà essere specificato nel file di migrazione quando lo modifichi.
Donturner

2
Creare --autoprima la migrazione è un ottimo consiglio. Evita problemi con il congelatore ORM sud, che si verificano se la migrazione ha solo metodi forwardse backwardsmetodi, ma non contiene l' modeloggetto congelato .
mwcz,

Come rispondo alla domanda di South "Dato che stai rimuovendo questo campo, DEVI specificare un valore predefinito"?
Bryce,

3
L'unico problema che ho con questo metodo è che db.rename_columnnon rinomina i vincoli associati alla colonna. La migrazione continuerà a funzionare ma avrai vincoli che prendono il nome dal vecchio nome della colonna. Avevo una colonna con un vincolo di unicità, ribattezzata con questo metodo, testato che il vincolo di unicità esisteva ancora e ottenevo un errore, ma il nome del vincolo stesso stava ancora usando il vecchio nome di colonna. Forse un esplicito db.delete_uniquee l' db.create_uniqueavrei fatto, ma ho deciso di andare con la soluzione di SJH.
Louis,

15

Non sapevo della colonna db.rename, sembra utile, tuttavia in passato ho aggiunto la nuova colonna come uno schema di migrazione, quindi ho creato una migrazione di dati per spostare i valori nel nuovo campo, quindi un secondo schema per rimuovere la vecchia colonna


Anch'io. Il problema con la tecnica schema-data-schema è che finisci con nomi diversi per i tuoi campi / modelli. A volte questo è un problema se usi nomi canonici per abilitare i dispatcher ...
Jonathan

Ho appena provato questo e non ha funzionato perché c'era un conflitto di nomi. Avevo esattamente lo stesso FK ma un nome diverso. Non convalidato. Quindi, non farlo.
Gezim,

2
@jonathan, vuole nomi diversi! ... @pilgrim, ti interessa pubblicare un codice? L'ho fatto diverse volte questa settimana, se i tuoi modelli non vengono convalidati, il sud non creerà le migrazioni.
sjh

Funzionerebbe, ma probabilmente sarebbe più lento del "rename_column" suggerito da altre persone. So che MySQL può fare una "ALTER TABLE ... rinomina colonna" (o qualunque cosa sia) abbastanza rapidamente.
Rory,

1
@Rory db.rename_columnnon rinominerà i vincoli per te, quindi devi gestirlo manualmente. Se si dimentica di farlo, la migrazione funzionerà, tranne per il fatto che a tua insaputa potrebbe esserci un vincolo che utilizza ancora il vecchio nome di colonna. Non mi è chiaro se il problema sia puramente estetico o se in una futura migrazione in cui il vincolo debba essere manipolato o eliminato a sud non sarà in grado di trovarlo. Ad ogni modo, farlo qui come suggerisce sjh è il modo sicuro per farlo: puoi lasciare che South capisca cosa dovrebbe capire.
Louis,

10

Django 1.7 ha introdotto Migrations quindi ora non è nemmeno necessario installare un pacchetto aggiuntivo per gestire le migrazioni.

Per rinominare il tuo modello devi prima creare una migrazione vuota:

$ manage.py makemigrations <app_name> --empty

Quindi è necessario modificare il codice della migrazione in questo modo:

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

E dopo devi eseguire:

$ manage.py migrate <app_name>

5

Basta cambiare il modello ed eseguire makemigrationsin 1.9

Django rileva automaticamente che hai eliminato e creato un singolo campo e chiede:

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

Dì di sì e viene creata la migrazione giusta. Magia.


0
  1. Aggiungi southalle app installate nel file delle impostazioni del progetto.
  2. Commenta il campo / tabella aggiunto / modificato.
  3. $ manage.py Schemamigration <app_name> --initial
  4. $ manage.py migrate <app_name> --Fake
  5. Annulla commento sul campo e scrivi quello modificato
  6. $ manage.py Schemamigration --auto
  7. $ manage.py migrate <app_name>

Se stai usando 'pycharm', puoi usare 'ctrl + shift + r' invece di 'manage.py' e 'shift' per i parametri.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.