Come ripristinare l'ultima migrazione?


448

Ho effettuato una migrazione che ha aggiunto una nuova tabella e desidero ripristinarla ed eliminare la migrazione, senza creare una nuova migrazione.

Come lo faccio? Esiste un comando per ripristinare l'ultima migrazione e quindi posso semplicemente eliminare il file di migrazione?

Risposte:


799

È possibile ripristinare migrando alla migrazione precedente.

Ad esempio, se le ultime due migrazioni sono:

  • 0010_previous_migration
  • 0011_migration_to_revert

Quindi faresti:

./manage.py migrate my_app 0010_previous_migration 

È quindi possibile eliminare la migrazione 0011_migration_to_revert.

Se stai usando Django 1.8+, puoi mostrare i nomi di tutte le migrazioni con

./manage.py showmigrations my_app

Per annullare tutte le migrazioni per un'app, è possibile eseguire:

./manage.py migrate my_app zero

7
Ho visto molte risposte su SO a questo problema che sono vecchie e semplicemente non funzionano più. +1 perché funziona con Django 1.8.
AlanSE

2
Come se l'app ha un solo file di migrazione / migrazione iniziale. e devo annullare quella migrazione iniziale?
Adiyat Mubarak,

37
Il migratecomando ti consente di ./manage.py migrate my_app zeroannullare l'applicazione di tutte le migrazioni per l'app.
Alasdair,

4
Per qualche motivo, ./manage.py migrare my_app 0010_previous_migration non ha funzionato per me. Quindi ho provato a usare ./manage.py migrate my_app 0010 e ha funzionato. Qualche motivo per cui Django 1.8 non accetta l'intero nome della migrazione?
Varun Verma,

4
Finché stai usando il tuo vero nome di migrazione e non '0010_previous_migration', non so perché vedresti quel comportamento.
Alasdair,

36

La risposta di Alasdair copre le basi

  • Identifica le migrazioni che desideri ./manage.py showmigrations
  • migrate utilizzando il nome dell'app e il nome della migrazione

Ma va sottolineato che non tutte le migrazioni possono essere invertite. Questo succede se Django non ha una regola per fare l'inversione. Per la maggior parte delle modifiche apportate automaticamente alle migrazioni ./manage.py makemigrations, l'inversione sarà possibile. Tuttavia, gli script personalizzati dovranno avere una scrittura sia in avanti che indietro, come descritto nell'esempio qui:

https://docs.djangoproject.com/en/1.9/ref/migration-operations/

Come eseguire un'inversione senza intervento

Se hai avuto RunPythonun'operazione, allora potresti voler semplicemente annullare la migrazione senza scrivere uno script di inversione logicamente rigoroso. La seguente modifica rapida all'esempio dai documenti (sopra link) consente ciò, lasciando il database nello stesso stato in cui si trovava dopo l'applicazione della migrazione, anche dopo averlo invertito.

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models

def forwards_func(apps, schema_editor):
    # We get the model from the versioned app registry;
    # if we directly import it, it'll be the wrong version
    Country = apps.get_model("myapp", "Country")
    db_alias = schema_editor.connection.alias
    Country.objects.using(db_alias).bulk_create([
        Country(name="USA", code="us"),
        Country(name="France", code="fr"),
    ])

class Migration(migrations.Migration):

    dependencies = []

    operations = [
        migrations.RunPython(forwards_func, lambda apps, schema_editor: None),
    ]

Funziona con Django 1.8, 1.9


Aggiornamento: un modo migliore per scrivere questo sarebbe quello di sostituirlo lambda apps, schema_editor: Nonecon migrations.RunPython.nooplo snippet sopra. Questi sono entrambi funzionalmente la stessa cosa. (merito ai commenti)


5
Da Django 1.8 in poi, dovresti usare al RunPython.noopposto di un lambda in linea o equivelent: docs.djangoproject.com/en/1.8/ref/migration-operations/…
SpoonMeiser,

@SpoonMeiser Nella sintassi dell'esempio, penso che assomigli migrations.RunPython(forwards_func, migrations.RunPython.noop). È necessario verificarlo funzionalmente. Questo dovrebbe essere aggiunto come risposta o modifica a questo prima o poi.
AlanSE,

13

Ecco la mia soluzione, poiché la soluzione di cui sopra non copre davvero il caso d'uso, quando si utilizza RunPython.

È possibile accedere alla tabella tramite ORM con

from django.db.migrations.recorder import MigrationRecorder

>>> MigrationRecorder.Migration.objects.all()
>>> MigrationRecorder.Migration.objects.latest('id')
Out[5]: <Migration: Migration 0050_auto_20170603_1814 for model>
>>> MigrationRecorder.Migration.objects.latest('id').delete()
Out[4]: (1, {u'migrations.Migration': 1})

Quindi puoi interrogare le tabelle ed eliminare quelle voci che sono rilevanti per te. In questo modo è possibile modificare in dettaglio. Con le RynPythonmigrazioni devi anche occuparti dei dati che sono stati aggiunti / modificati / rimossi. L'esempio sopra mostra solo come si accede alla tabella tramite Djang ORM.


Quando si creano nuovi modelli con ForeignKeys, con più migrazioni, la realizzazione di tutto è errata e il riavvio di 2-3 migrazioni all'indietro, con nuovi modelli ma a volte stessi nomi di modello o stessi nomi di relazione ... questa soluzione è chiaramente la vincente. Ho ricevuto un messaggio di errore come quello in django.db.utils.ProgrammingError: relation "<relation name>" already existscui ho fatto un migrate --fakeerrore, quindi ho provato a tornare indietro, quindi ho ricevuto psycopg2.ProgrammingError: relation "<other <relation name>" does not existGRAZIE
onekiloparsec,

10

L'altra cosa che puoi fare è eliminare la tabella creata manualmente.

Insieme a questo, dovrai eliminare quel particolare file di migrazione. Inoltre, dovrai eliminare quella particolare voce nella tabella django-migrations (probabilmente l'ultima nel tuo caso) che è correlata a quella particolare migrazione.


fai attenzione in questo caso - sei obbligato a verificare che db sia adeguato.
Sławomir Lenart,

4
Aggiungerei MOLTO attento. In Postgres potresti rompere molte cose, ad esempio i vincoli.
joedborg,

9

Non eliminare il file di migrazione fino a dopo l'inversione. Ho fatto questo errore e senza il file di migrazione, il database non sapeva quali cose rimuovere.

python manage.py showmigrations
python manage.py migrate {app name from show migrations} {00##_migration file.py}

Elimina il file di migrazione. Una volta che la migrazione desiderata è nei tuoi modelli ...

python manage.py makemigrations
python manage.py migrate

8

L'ho fatto in 1.9.1 (per eliminare l'ultima o l'ultima migrazione creata):

  1. rm <appname>/migrations/<migration #>*

    esempio: rm myapp/migrations/0011*

  2. ha effettuato l'accesso al database ed eseguito questo SQL (postgres in questo esempio)

    delete from django_migrations where name like '0011%';

Sono stato quindi in grado di creare nuove migrazioni che sono iniziate con il numero di migrazione che avevo appena eliminato (in questo caso, 11).


1
+1 Anche se funzionerà, è necessario salvare in questo modo come ultima risorsa. Inoltre, è necessario ricordare di modificare / eliminare colonne / tabelle di cui ha contribuito la migrazione problematica.
Nehem,

buon punto - l'ho usato quando ho creato una migrazione ma non ho ancora eseguito "./manage.py migrate"
MIkee

3

Questa risposta è per casi simili se la risposta migliore di Alasdair non aiuta . (Ad esempio se la migrazione indesiderata viene creata di nuovo presto con ogni nuova migrazione o se si trova in una migrazione più grande che non può essere ripristinata o la tabella è stata rimossa manualmente.)

... eliminare la migrazione, senza creare una nuova migrazione?

TL; DR : è possibile eliminare alcune ultime migrazioni ripristinate (confuse) ed eseguirne una nuova dopo aver corretto i modelli . È inoltre possibile utilizzare altri metodi per configurarlo in modo da non creare una tabella tramite il comando migrate. L'ultima migrazione deve essere creata in modo che corrisponda ai modelli correnti .


Casi per cui qualcuno non vuole creare una tabella per un modello che deve esistere:

A) Nessuna tabella di questo tipo dovrebbe esistere in nessun database su nessuna macchina e senza condizioni

  • Quando: è un modello di base creato solo per l'eredità del modello di un altro modello.
  • Soluzione: impostareclass Meta: abstract = True

B) La tabella viene creata raramente, da qualcos'altro o manualmente in modo speciale.

  • Soluzione: utilizzare class Meta: managed = False
    La migrazione viene creata, ma mai utilizzata, solo nei test. Il file di migrazione è importante, altrimenti i test del database non possono essere eseguiti, a partire dallo stato iniziale riproducibile.

C) La tabella viene utilizzata solo su alcune macchine (ad es. In fase di sviluppo).

  • Soluzione: spostare il modello in una nuova applicazione che viene aggiunta a INSTALLED_APPS solo in condizioni speciali o utilizzare un condizionale class Meta: managed = some_switch.

D) Il progetto utilizza più database insettings.DATABASES

  • Soluzione: scrivere un router di database con il metodo allow_migrateper differenziare i database in cui la tabella deve essere creata e dove no.

La migrazione viene creata in tutti i casi A), B), C), D) con Django 1.9+ (e solo nei casi B, C, D con Django 1.8), ma applicata al database solo nei casi appropriati o forse mai se richiesto così. Le migrazioni sono state necessarie per l'esecuzione dei test da Django 1.8. Lo stato corrente rilevante completo viene registrato dalle migrazioni anche per i modelli con managed = False in Django 1.9+ per poter creare un ForeignKey tra modelli gestiti / non gestiti o per rendere il modello gestito = Vero in seguito. (Questa domanda è stata scritta al tempo di Django 1.8. Tutto qui dovrebbe essere valido per le versioni dalla 1.8 alla 2.2 attuale.)

Se l'ultima migrazione non è (non è) facilmente ripristinabile ./manage.py migrate --fake my_app 0010_previous_migration , è possibile eseguire con cautela (dopo il backup del database) un ripristino falso , eliminare manualmente la tabella.

Se necessario, creare una migrazione fissa dal modello fisso e applicarla senza modificare la struttura del database ./manage.py migrate --fake my_app 0011_fixed_migration.


3

Se stai riscontrando problemi durante il ripristino della migrazione e in qualche modo hai pasticciato, puoi eseguire le fakemigrazioni.

./manage.py migrate <name> --ignore-ghost-migrations --merge --fake

Per la versione django <1.7 questo creerà la voce nella south_migrationhistorytabella, è necessario eliminare quella voce.

Ora sarai in grado di ripristinare facilmente la migrazione.

PS: Sono rimasto bloccato per molto tempo e ho eseguito una migrazione fasulla e poi tornare indietro mi ha aiutato.


1
Questa risposta è per Django <1.7.
Hynekcer,
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.