2015-03-05 19 views
12

Se ho un campo Modello non annullabile, rimuoverlo, e creare una migrazione, che la migrazione diventa irreversibile:Revert Django 1.7 RemoveField migrazione

Si consideri il seguente modello:

class Foo(models.Model): 
    bar = models.TextField() 
    test = models.TextField() # This field is to go away, bye-bye! 

E migrazione:

# app/migrations/003_remove_foo_test.py 

class Migration(migrations.Migration): 

    dependencies = [ 
     ('app', '0002_foo_test'), 
    ] 

    operations = [ 
     migrations.RemoveField(
      model_name='foo', 
      name='test', 
     ), 
    ] 

Unapplying questa migrazione genera un'eccezione:

$ src/manage.py migrate app 0002 
Operations to perform: 
    Target specific migration: 0002_foo_test, from app 
Running migrations: 
    Unapplying app.0003_remove_foo_test...Traceback (most recent call last): 
... 
django.db.utils.IntegrityError: column "test" contains null values 

Naturalmente, questo è il comportamento previsto, è clearly documented e non sto chiedendo perché questo accade:

Tenete a mente che, quando ha invertito questo è in realtà l'aggiunta di un campo per un modello ; se il campo non è annullabile, è possibile che questa operazione sia irreversibile (a parte qualsiasi perdita di dati, che ovviamente è irreversibile).

Tuttavia, tutti facciamo degli errori e, a volte abbiamo solo bisogno -qualche modo invertire una delezione campo, anche se questo significa fornire manualmente un valore ad hoc stub per tutti i campi non nulli invertiti. Ad esempio, le migrazioni Sud consentono facoltativamente l'inversione di tali operazioni (chiedendo allo sviluppatore se fornire un valore predefinito per i campi ripristinati, o non consentire la migrazione inversa), il che non sembra essere il caso delle nuove migrazioni di Django 1.7 .

Domanda: qual è il modo più semplice/veloce per annullare la rimozione di un campo con le migrazioni di Django 1.7+ (supponendo che sia già successo)? Non ha necessariamente bisogno di essere completamente scriptato con Python, un insieme di istruzioni manuali funzionerà.

risposta

11

È possibile cato manualmente è la tua migrazione e aggiungi AlterField con valore predefinito per un campo poco prima di RemoveField. Dovrebbe essere sicuro anche dopo aver applicato la migrazione.Questo renderà RemoveField che avverrà dopo essere reversibile.

Un esempio. Avendo campo nel modello di summary chiamato profit che è stato definito prima della rimozione del genere:

profit = models.PositiveIntegerField(verbose_name='profits') 

si dovrebbe aggiungere, prima RemoveField di esso un AlterField così:

migrations.AlterField(
    model_name='summary', 
    name='profit', 
    field=models.PositiveIntegerField(verbose_name='profits', default=0), 
    preserve_default=False, 
    ), 
+0

'AlterField' non accetta l'argomento' preserve_default' (ma non sembra essere necessario in questo caso, dato che 'default' non raggiunge comunque lo schema del database). Oltre a ciò, questa sembra essere una soluzione corretta e ottimale. Grazie! –

+0

Secondo [documentazione] (https://docs.djangoproject.com/en/1.7/ref/migration-operations/#django.db.migrations.operations.AlterField), sì, ma è stato aggiunto in 1.7. 1. E in tal caso, preserve_default non dovrebbe avere importanza. Arriva al database, ma solo per un breve periodo. – GwynBleidD

+0

Questo non sembra funzionare in Django 1.8 – jess

3

Se si sta tentando di rendere reversibili le migrazioni future, è possibile provare a rimuovere il campo come tre migrazioni.

  1. rendere il campo nullable
  2. migrazione dei dati. Avanti, non fare nulla. Indietro, converte i null in un valore di stub.
  3. rimuovere il campo

Ciascuna di queste tre fasi dovrebbe essere reversibile.

Se avete già eseguito la migrazione e la necessità di invertire tale tendenza, si potrebbe

  1. aggiungere manualmente il campo, permettendo null
  2. convertono valori nulli per un valore stub
  3. aggiungere manualmente il vincolo NOT NULL
  4. Migrate con --fake alla migrazione precedente
+0

Io non sono molto sicuro di "aggiungere manualmente il campo "al punto 1. Mi piacerebbe sicuramente evitare di eseguire manualmente ALTER TABLE .. ADD COLUMN, questo è quello che lo strato ORM fa sempre e dovrebbe sempre fare. Non è compito del programmatore ripetere tutti i piccoli trucchi che l'ORM fa (denominazione dei campi, gestione degli indici, ecc.) –

+0

L'esecuzione manuale di SQL non è l'ideale, ma a volte potresti non avere altre opzioni. Qualcun altro potrebbe avere un suggerimento migliore. Potrebbe essere possibile ottenere Django per generare l'SQL con './manage.py sqlmigrate --backwards'. – Alasdair

+0

Posso confermare che './manage.py sqlmigrate --backwards' fornisce l'SQL corretto per eseguire questa migrazione e che questo non è doloroso da eseguire se si utilizza un IDE come PyCharm; l'approccio di modifica delle migrazioni non ha funzionato per me su una migrazione all'indietro. – Symmetric