Як відновити останню міграцію?


447

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

Як це зробити? Чи є команда повернути останню міграцію, і тоді я можу просто видалити файл міграції?

Відповіді:


796

Ви можете повернутись, перейшовши на попередню міграцію.

Наприклад, якщо ваші останні дві міграції:

  • 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
Я бачив багато відповідей на цю проблему, які є старими і просто більше не працюють. +1, оскільки це працює з Django 1.8.
AlanSE

2
Як, якщо у додатку є лише один файл міграції / початкова міграція. і мені потрібно скасувати цю початкову міграцію?
Адіят Мубарак

37
migrateКоманда дозволяє вам використовувати ./manage.py migrate my_app zeroдля всіх міграцій Виключити для додатка.
Alasdair

4
Чомусь ./manage.py міграція 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 використовуючи ім’я програми та ім’я міграції

Але слід зазначити, що не всі міграції можна змінити. Це трапляється, якщо у Джанго немає правила робити реверс. Для більшості змін, до яких ви автоматично здійснили міграцію ./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в сниппета вище. Це обоє функціонально одне і те ж. (заслуга в коментарях)


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 (можливо, останній у вашому випадку), який співвідноситься з певною міграцією.


будьте обережні в цьому випадку - ви зобов’язані перевірити db, щоб бути адекватним.
Славомір Ленарт

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

8

Я зробив це в 1.9.1 (щоб видалити останню або останню створену міграцію):

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

    приклад: rm myapp/migrations/0011*

  2. увійшли в базу даних і запустили цей SQL (постгреси в цьому прикладі)

    delete from django_migrations where name like '0011%';

Тоді мені вдалося створити нові міграції, які почалися з міграційного номера, який я щойно видалив (у даному випадку 11).


1
+1 Хоча це і спрацює, вам потрібно зберегти цей шлях в крайньому випадку. Крім того, ви повинні пам'ятати, щоб редагувати / відкидати стовпці / таблиці, які сприяли проблематичній міграції.
nehem

хороший момент - я використав це, коли створив міграцію, але ще не запустив "./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


Міграція створюється у всіх випадках A), B), C), D) з Django 1.9+ (і лише у випадках B, C, D з Django 1.8), але застосовується до бази даних лише у відповідних випадках або, можливо, ніколи, якщо потрібно так. Міграція необхідна для запуску тестів з Джанго 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.


3

Якщо під час повернення назад міграції ви стикаєтеся з проблемою, і хоч якось зіпсували її, ви можете виконати fakeміграцію.

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

Для версії django <1.7 це створить запис у south_migrationhistoryтаблиці, його потрібно видалити.

Тепер ви зможете легко повернути міграцію.

PS: Я застряг багато часу, і фальшива міграція, а потім повернення назад допомогло мені.


1
Ця відповідь стосується Джанго <1,7.
hynekcer
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.