Il modo più semplice per rinominare un modello usando Django / South?


141

Ho cercato una risposta a questo sul sito di South, Google e SO, ma non sono riuscito a trovare un modo semplice per farlo.

Voglio rinominare un modello Django usando South. Di 'che hai il seguente:

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

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

e vuoi convertire Foo in Bar, vale a dire

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

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

Per semplificare, sto solo cercando di cambiare il nome da Fooin Bar, ma ignora il foomembro FooTwoper ora.

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

  1. Probabilmente potrei fare una migrazione dei dati, ma sembra piuttosto coinvolto.
  2. Scrivi una migrazione personalizzata, ad esempio db.rename_table('city_citystate', 'geo_citystate'), ma non sono sicuro di come risolvere la chiave esterna in questo caso.
  3. Un modo più semplice che conosci?

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

Soluzione ottimizzata per Django> = 1.8 stackoverflow.com/questions/25091130/…
Programmatore chimico

Risposte:


130

Per rispondere alla tua prima domanda, il semplice cambio di modello / tabella è piuttosto semplice. Esegui il comando:

./manage.py schemamigration yourapp rename_foo_to_bar --empty

(Aggiornamento 2: prova --autoinvece di --emptyevitare l'avvertenza di seguito. Grazie a @KFB per il suggerimento.)

Se stai usando una versione precedente del sud, ti servirà startmigrationinvece di schemamigration.

Quindi modificare manualmente il file di migrazione per assomigliare a questo:

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

Puoi farlo più semplicemente usando l' db_tableopzione Meta nella tua classe di modello. Ma ogni volta che lo fai, aumenti il ​​peso legacy della tua base di codice - avere nomi di classe diversi dai nomi di tabella rende il tuo codice più difficile da capire e mantenere. Sostengo pienamente facendo semplici rifatturazioni come questa per motivi di chiarezza.

(aggiornamento) Ho appena provato questo in produzione e ho ricevuto uno strano avvertimento quando sono andato ad applicare la migrazione. Ha detto:

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

Ho risposto "no" e tutto sembrava andare bene.


3
Sono stato in grado di evitare il messaggio di errore di Leopd creando la migrazione dello schema usando --auto anziché --empty. Ho quindi modificato il file di migrazione, modificando la cancellazione / creazione delle tabelle in una chiamata db.rename_table (). Questo sembra aver funzionato molto bene.
KFB,

4
Ho usato questa tecnica il 9/2/2011 senza ottenere errori. Forse una versione più recente di South ha risolto il problema con gli errori.
Chip Tol

1
Grazie per averlo aggiornato! La risposta di Jian in basso dice che è importante mantenere le chiamate "send_create_signal", ne sai qualcosa? Se sei d'accordo, sarebbe bello aggiornare la tua migrazione di esempio.
mrooney,

5
Attenzione che questo non rinominerà gli indici su quella tabella. Se in futuro crei una nuova tabella con lo stesso nome della vecchia tabella, puoi ottenere errori in caso di collisione dei nomi degli indici. Stavamo usando questa tecnica, ma d'ora in poi creeremo esplicitamente la nuova tabella, migreremo i dati, quindi elimineremo la vecchia tabella.
Jeremy Banks,

3
Anche i nomi delle colonne nelle tabelle generate automaticamente, come le tabelle M2M nel modello originale, non vengono migrati con questo metodo.
spookylukey,

66

Apporta le modifiche models.pye quindi esegui

./manage.py schemamigration --auto myapp

Quando controlli il file di migrazione, vedrai che elimina una tabella e ne crea una nuova

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

Questo non è proprio quello che vuoi. Invece, modifica la migrazione in modo che assomigli a:

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 assenza updatedell'istruzione, la db.send_create_signalchiamata creerà una nuova ContentTypecon il nome del nuovo modello. Ma è meglio solo updatequello che ContentTypehai già nel caso in cui ci siano oggetti di database che puntano ad esso (ad esempio, tramite a GenericForeignKey).

Inoltre, se hai rinominato alcune colonne che sono chiavi esterne del modello rinominato, non dimenticare di farlo

db.rename_column(myapp_model, foo_id, bar_id)

2
Viene visualizzato l'errore KeyError: "Il modello 'contenttype' dall'app 'contenttypes' non è disponibile in questa migrazione." Inoltre, ho una tabella django_content_type, ma non una tabella contenttypes. (Django 1.6)
Seth

2
@Seth Ho lavorato in questo modo eseguendo l'aggiornamento dei modelli ContentType in una migrazione di dati separata e aggiungendo il contenttypes.ContentTypemodello ai modelli congelati utilizzando il --frozenflag in ./manage.py datamigration. Ad esempio: ./manage.py datamigration --frozen contenttypes myapp update_contenttypes. Quindi modifica myapp_migrations / NNNN_update_contenttypes.py con il codice di aggiornamento del tipo di contenuto come specificato sopra.
Geoffrey Hing,

@GeoffreyHing Penso che il parametro non sia congelato né congelato. south.readthedocs.io/en/latest/ormfreezing.html Ma grazie mille per il tuo aiuto, è stato davvero utile.
ccsakuweb,

5

Il Sud non può farlo da solo - come fa a sapere che Barrappresenta ciò che era Foosolito fare? Questo è il genere di cose per cui scriverei una migrazione personalizzata. Puoi modificare il tuo ForeignKeycodice in come hai fatto sopra, e quindi è solo un caso di rinominare i campi e le tabelle appropriate, che puoi fare come vuoi.

Infine, hai davvero bisogno di farlo? Devo ancora dover rinominare i modelli - i nomi dei modelli sono solo un dettaglio dell'implementazione - in particolare data la disponibilità verbose_namedell'opzione Meta.


7
In alternativa, rinominare il modello nel codice ma utilizzare l' db_tableopzione Meta per mantenere lo stesso nome della tabella del database.
Daniel Roseman,

@Daniel: sai se db_tableviene utilizzato per derivare nomi di chiavi esterne?
Dominic Rodger,

lo credo. Se si modifica il nome del modello e si imposta db_table, tutto dovrebbe funzionare come previsto.
Davor Lucic,

1
@DanielRoseman questa è la soluzione migliore in tutto il thread!
Joerick,

-1

Ho seguito la soluzione di Leopd sopra. Ma ciò non ha cambiato i nomi dei modelli. L'ho modificato manualmente nel codice (anche nei modelli correlati in cui questo è indicato come FK). E fatto un'altra migrazione a sud, ma con l'opzione --fake. Ciò rende i nomi dei modelli e dei nomi delle tabelle uguali.

Appena realizzato, si potrebbe iniziare con la modifica dei nomi dei modelli, quindi modificare il file delle migrazioni prima di applicarle. Molto più pulito.

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.