Как отменить последнюю миграцию?

448

Я выполнил миграцию, добавив новую таблицу, и хочу отменить ее и удалить миграцию, не создавая новую миграцию.

Как это сделать? Есть ли команда, чтобы отменить последнюю миграцию, и тогда я могу просто удалить файл миграции?

Ронен Несс
источник

Ответы:

799

Вы можете вернуться, перейдя к предыдущей миграции.

Например, если последние две миграции:

  • 0010_previous_migration
  • 0011_migration_to_revert

Тогда вы бы сделали:

./manage.py migrate my_app 0010_previous_migration 

Затем вы можете удалить миграцию 0011_migration_to_revert.

Если вы используете Django 1.8+, вы можете показать имена всех миграций с помощью

./manage.py showmigrations my_app

Чтобы отменить все миграции для приложения, вы можете запустить:

./manage.py migrate my_app zero
Алэсдэйр
источник
7
Я видел много ответов на SO на эту проблему, которые старые и просто больше не работают. +1, потому что это работает с Django 1.8.
AlanSE
2
Как, если приложение имеет только один файл миграции / начальная миграция. и мне нужно отменить эту первоначальную миграцию?
Адият Мубарак
37
Команда migrate, которую вы будете использовать, ./manage.py migrate my_app zeroчтобы отменить все миграции для приложения.
Alasdair
4
По какой-то причине ./manage.py migrate my_app 0010_previous_migration не работает для меня. Поэтому я попытался использовать ./manage.py migrate my_app 0010, и это сработало. Какие-либо причины, по которым Django 1.8 не принимает полное имя миграции?
Варун Верма
4
Пока вы используете свое настоящее имя миграции, а не '0010_previous_migration'я, я не знаю, почему вы бы увидели такое поведение.
Alasdair
36

Ответ от Alasdair охватывает основы

  • Определите миграции, которые вы хотите ./manage.py showmigrations
  • migrate используя имя приложения и имя миграции

Но следует отметить, что не все миграции можно повернуть вспять. Это происходит, если у Django нет правила для аннулирования. Для большинства изменений, на которые вы автоматически произвели миграцию ./manage.py makemigrations, будет возможно изменение. Тем не менее, пользовательские сценарии должны иметь прямую и обратную запись, как описано в следующем примере:

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

Как сделать отмену операции

Если у вас была RunPythonоперация, то, возможно, вы просто хотите отказаться от миграции, не написав логически строгий сценарий реверсирования. Следующий быстрый взлом к ​​примеру из документации (ссылка выше) позволяет это сделать, оставив базу данных в том же состоянии, в каком она была после применения миграции, даже после ее отмены.

# -*- 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),
    ]

Это работает для Django 1.8, 1.9


Обновление: Лучший способ написания этого было бы заменить lambda apps, schema_editor: Noneс migrations.RunPython.noopв сниппета выше. Оба они функционально одно и то же. (кредит к комментариям)

AlanSE
источник
5
Начиная с Django 1.8, вы должны использовать RunPython.noopвместо встроенной лямбды или равнозначной: docs.djangoproject.com/en/1.8/ref/migration-operations/…
SpoonMeiser
@SpoonMeiser В синтаксисе примера, я думаю, это выглядит так migrations.RunPython(forwards_func, migrations.RunPython.noop). Нужно проверить это функционально. Это должно быть добавлено как ответ или редактирование этого когда-нибудь.
AlanSE
13

Вот мое решение, так как вышеупомянутое решение действительно не покрывает случай использования, когда вы используете RunPython.

Вы можете получить доступ к таблице через ORM с

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

Таким образом, вы можете запросить таблицы и удалить те записи, которые имеют отношение к вам. Таким образом, вы можете изменить подробно. При RynPythonмиграции вам также необходимо позаботиться о данных, которые были добавлены / изменены / удалены. Приведенный выше пример показывает только то, как вы получаете доступ к таблице через Djang ORM.

Озер С.
источник
При создании новых моделей с помощью ForeignKeys, с несколькими миграциями, понимая, что все неправильно, и перезапуская 2-3 миграции в обратном направлении, с новыми моделями, но иногда с теми же именами моделей или теми же именами отношений ... это решение явно выигрывает. У меня было сообщение об ошибке, как, например, django.db.utils.ProgrammingError: relation "<relation name>" already existsя сделал что-то, migrate --fakeчто неправильно, поэтому я попытался вернуться, затем я получил psycopg2.ProgrammingError: relation "<other <relation name>" does not existСПАСИБО
onekiloparsec
10

Другая вещь, которую вы можете сделать, это удалить таблицу, созданную вручную.

Наряду с этим вам придется удалить этот конкретный файл миграции. Кроме того, вам придется удалить конкретную запись в таблице django-migrations (возможно, последнюю в вашем случае), которая соответствует этой конкретной миграции.

sprksh
источник
будьте осторожны в этом случае - вы обязаны проверить, что БД является адекватным.
Славомир Ленарт
4
Я бы добавил ОЧЕНЬ осторожно. Вы можете сломать много вещей в Postgres, например, ограничения.
Джоедборг
9

Не удаляйте файл миграции до тех пор, пока не вернетесь. Я сделал эту ошибку и без файла миграции, база данных не знала, что удалить.

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

Удалить файл миграции. Как только желаемая миграция появится в ваших моделях ...

python manage.py makemigrations
python manage.py migrate
DanGoodrick
источник
8

Я сделал это в 1.9.1 (чтобы удалить последнюю или последнюю созданную миграцию):

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

    пример: rm myapp/migrations/0011*

  2. вошел в базу данных и запустил этот SQL (postgres в этом примере)

    delete from django_migrations where name like '0011%';

Затем я смог создать новые миграции, которые начинались с номера миграции, который я только что удалил (в данном случае 11).

Mikee
источник
1
+1 Хотя это сработает, вам нужно сохранить этот способ в крайнем случае. Также вы должны помнить, чтобы редактировать / удалять столбцы / таблицы, из-за которых возникла проблемная миграция.
Нехем,
Хорошая мысль - я использовал это, когда создал миграцию, но еще не запустил "./manage.py migrate"
MIkee
3

Этот ответ для подобных случаев, если топовый ответ от Alasdair не помогает . (Например, если нежелательная миграция создается снова снова при каждой новой миграции, или если она находится в более крупной миграции, которую нельзя отменить, или таблица была удалена вручную.)

... удалить миграцию, не создавая новую миграцию?

TL; DR : Вы можете удалить несколько последних отмененных (запутанных) миграций и создать новую после исправления моделей . Вы также можете использовать другие методы для его настройки, чтобы не создавать таблицу с помощью команды migrate. Последняя миграция должна быть создана так, чтобы она соответствовала текущим моделям .


Случаи, когда кто-то не хочет создавать таблицу для Модели, которая должна существовать:

A) Никакой такой таблицы не должно быть ни в одной базе данных, ни на машине, ни при каких условиях

  • Когда: это базовая модель, созданная только для наследования модели другой модели.
  • Решение: Установитьclass Meta: abstract = True

Б) Таблица создается редко, чем-то другим или вручную особым образом.

  • Решение: Использование class Meta: managed = False
    Миграция создается, но никогда не используется, только в тестах. Файл миграции важен, иначе тесты базы данных не могут быть запущены, начиная с воспроизводимого исходного состояния.

C) Таблица используется только на некоторых машинах (например, в разработке).

  • Решение. Переместите модель в новое приложение, которое добавляется в INSTALLED_APPS только при особых условиях, или используйте условное условие class Meta: managed = some_switch.

D) Проект использует несколько баз данных вsettings.DATABASES

  • Решение: Напишите маршрутизатор базы данных с методом allow_migrate, чтобы различать базы данных, где таблица должна быть создана, а где нет.

Миграция создается во всех случаях A), B), C), D) с Django 1.9+ (и только в случаях B, C, D с Django 1.8), но применяется к базе данных только в соответствующих случаях или, возможно, никогда, если требуется так. Миграции были необходимы для запуска тестов начиная с Django 1.8. Полное релевантное текущее состояние регистрируется миграциями даже для моделей с управляемого = False в Django 1.9+, чтобы можно было создать ForeignKey между управляемыми / неуправляемыми моделями или сделать модель управляемой = True позже. (Этот вопрос был написан во времена Django 1.8. Здесь все должно быть актуально для версий от 1.8 до текущей версии 2.2.)

Если последняя миграция не является (легко) обратимой, то можно осторожно (после резервного копирования базы данных) сделать ложный возврат ./manage.py migrate --fake my_app 0010_previous_migration , удалить таблицу вручную.

При необходимости создайте фиксированную миграцию из фиксированной модели и примените ее без изменения структуры базы данных ./manage.py migrate --fake my_app 0011_fixed_migration.

hynekcer
источник
3

Если вы столкнулись с проблемой при возврате миграции и каким-то образом испортили ее, вы можете выполнить fakeмиграцию.

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

Для версии django <1.7 это создаст запись в south_migrationhistoryтаблице, вам нужно удалить эту запись.

Теперь вы сможете легко вернуться к миграции.

PS: я застрял на долгое время, и мне пришлось выполнить ложную миграцию, а затем вернуться назад.

Пранш Тивари
источник
1
Этот ответ для Джанго <1,7.
Hynekcer