Что такое исключение Django South GhostMigrations и как его отлаживать?

Внес некоторые изменения в модель моего приложения Django и использовал South для их переноса на мою машину разработки (миграции с 0004 по 0009). Но при попытке перенести эти изменения на сервер я получаю ошибку "GhostMigrations".

Не так много хорошего контента, объясняющего, что такое фантомная миграция или как ее отлаживать. Google не помог в этом, и другие вопросы SO, в которых упоминаются призрачные миграции, также не охватывают это (самый полезный вопрос здесь в основном речь шла о рабочем процессе). Полезные люди в IRC django-south сказали следующее о призрачных миграциях: «это означает, что история юга (таблица в базе данных) записывает две миграции, которые, по его мнению, были применены, но файлы миграции которых он не может найти» . Я пытаюсь выяснить сейчас, как завершить отладку.

Заранее спасибо за помощь.

Вот ошибка:

Traceback (most recent call last):
  File "manage.py", line 14, in <module>
    execute_manager(settings)
  File "/home/username/webapps/myproject/lib/python2.6/django/core/management/__init__.py", line 438, in execute_manager
    utility.execute()
  File "/home/username/webapps/myproject/lib/python2.6/django/core/management/__init__.py", line 379, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/username/webapps/myproject/lib/python2.6/django/core/management/base.py", line 191, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/home/username/webapps/myproject/lib/python2.6/django/core/management/base.py", line 220, in execute
    output = self.handle(*args, **options)
  File "/home/username/lib/python2.6/South-0.7.3-py2.6.egg/south/management/commands/migrate.py", line 105, in handle
    ignore_ghosts = ignore_ghosts,
  File "/home/username/lib/python2.6/South-0.7.3-py2.6.egg/south/migration/__init__.py", line 171, in migrate_app
    applied = check_migration_histories(applied, delete_ghosts, ignore_ghosts)
  File "/home/username/lib/python2.6/South-0.7.3-py2.6.egg/south/migration/__init__.py", line 88, in check_migration_histories
    raise exceptions.GhostMigrations(ghosts)
south.exceptions.GhostMigrations: 

 ! These migrations are in the database but not on disk:
    <bodyguard: 0002_auto__add_field_asset_is_reserved__add_field_asset_is_created__add_fie>
    <bodyguard: 0003_auto__del_field_asset_is_reserved__add_field_asset_is_assigned>
 ! I'm not trusting myself; either fix this yourself by fiddling
 ! with the south_migrationhistory table, or pass --delete-ghost-migrations
 ! to South to have it delete ALL of these records (this may not be good).

Я был удивлен, увидев, что Юг жаловался на миграции 0002 и 0003, потому что я внес эти изменения несколько месяцев назад. Изменения, которые я внес ранее сегодня, были изменениями с 0004 по 0009.

Вот моя модель:

class Asset(models.Model):
    title = models.CharField(max_length=200, blank=True, null=True)
    user = models.ForeignKey(User, blank=True, null=True) 
    is_assigned = models.NullBooleanField(blank=True, null=True) 
    is_created = models.NullBooleanField(blank=True, null=True) 
    is_active = models.NullBooleanField(blank=True, null=True)
    activation_date = models.DateTimeField(default=datetime.datetime.now, blank=True, null=True)

class AssetEdit(models.Model):
    asset = models.ForeignKey(Asset, related_name="edits", blank=True, null=True)
    update_date = models.DateTimeField(default=datetime.datetime.now, blank=True, null=True)

Вот содержимое папки южных миграций:

0001_initial.py
0001_initial.pyc
0002_auto__chg_field_asset_username__chg_field_asset_title__chg_field_asset.py
0002_auto__chg_field_asset_username__chg_field_asset_title__chg_field_asset.pyc
0003_auto__add_field_asset_is_reserved__add_field_asset_is_created__add_fie.py
0003_auto__add_field_asset_is_reserved__add_field_asset_is_created__add_fie.pyc
0004_auto__del_field_asset_is_reserved__add_field_asset_is_assigned.py
0004_auto__del_field_asset_is_reserved__add_field_asset_is_assigned.pyc
0005_auto__add_assetedit.py
0005_auto__add_assetedit.pyc
0006_auto__del_field_assetedit_user__add_field_assetedit_asset.py
0006_auto__del_field_assetedit_user__add_field_assetedit_asset.pyc
0007_auto__chg_field_assetedit_update_date.py
0007_auto__chg_field_assetedit_update_date.pyc
0008_auto__add_field_asset_activated_date.py
0008_auto__add_field_asset_activated_date.pyc
0009_auto__del_field_asset_activated_date__add_field_asset_activation_date.py
0009_auto__del_field_asset_activated_date__add_field_asset_activation_date.pyc
__init__.py
__init__.pyc

Это таблица south_migrationtable:

 id | app_name  |                                  migration                                  |            applied            
----+-----------+-----------------------------------------------------------------------------+-------------------------------
  1 | myapp     | 0001_initial                                                                | 2011-10-14 22:07:11.467184-05
  2 | myapp     | 0002_auto__add_field_asset_is_reserved__add_field_asset_is_created__add_fie | 2011-10-14 22:07:11.469822-05
  3 | myapp     | 0003_auto__del_field_asset_is_reserved__add_field_asset_is_assigned         | 2011-10-14 22:07:11.471799-05
(3 rows)

Это таблица myapp_asset в ее нынешнем виде:

                                   Table "public.myapp_asset"
   Column    |          Type          |                          Modifiers                           
-------------+------------------------+--------------------------------------------------------------
 id          | integer                | not null default nextval('myapp_asset_id_seq'::regclass)
 title       | character varying(200) | 
 user_id     | integer                | 
 is_assigned | boolean                | 
 is_created  | boolean                | 
 is_active   | boolean                | 
Indexes:
    "myapp_asset_pkey" PRIMARY KEY, btree (id)
    "myapp_asset_user_id" btree (user_id)
Foreign-key constraints:
    "myapp_asset_user_id_fkey" FOREIGN KEY (user_id) REFERENCES auth_user(id) DEFERRABLE INITIALLY DEFERRED

Я не могу понять, почему django-south считает миграции 0002 и 0003 "призраками". Оба они находятся в папке миграции, указаны как «примененные» в таблице миграции, и база данных, похоже, соответствует конечному состоянию после миграции 0003.

(возможные ошибки: папка миграции была включена в репозиторий git; миграция 0002 создала атрибут, а затем 0003 переименовала его)


person jchung    schedule 16.01.2012    source источник


Ответы (2)


Каким-то образом ваша база данных записала миграции 0002 и 0003, которые она не может найти в вашей папке миграций.

Миграция 0002 в вашей файловой системе 0002_auto__chg_field_asset_username__chg_field_asset_title__chg_field_asset.py, а в таблице истории 0002_auto__add_field_asset_is_reserved__add_field_asset_is_created__add_fie.py

Юг, должно быть, был перенесен, когда ваша папка миграции имела другое содержимое (возможно, в разработке?).

Основываясь на том, что вы говорите, мне кажется, что ваша база данных отражает состояние при миграции 0004, поэтому я бы запустил python manage.py migrate myapp 0004 --fake --delete-ghost-migrations, который установит таблицу миграции в точку, в которую вы добавили поле is_assigned, и вы можете с радостью применять миграции 0005+.

Вам лучше знать, какой миграции должна соответствовать текущая таблица БД!

person Yuji 'Tomita' Tomita    schedule 16.01.2012
comment
Спасибо Юджи. Быстрый вопрос: что именно происходит в --delete-ghost-migrations? South просто удаляет эти строки из south_migrationtable и заменяет их правильными строками? Я спрашиваю, потому что поля, созданные в миграциях 0002 и 0003, уже заполнены. Юг не удалит эти данные и не создаст их заново, не так ли? - person jchung; 16.01.2012
comment
@ Джаред, нет, это изменит только таблицу south_migrationhistory. Вам все равно нужно указать --fake, потому что югу не удастся применить реальную миграцию 2, 3 и 4, поскольку ваша БД уже отражает эти изменения (или, возможно, даже больше, но я не могу сказать только по вашим примерам кода) - person Yuji 'Tomita' Tomita; 16.01.2012

Они считаются фантомными миграциями, потому что имена в базе данных:

0002_auto__add_field_asset_is_reserved__add_field_asset_is_created__add_fie
0003_auto__del_field_asset_is_reserved__add_field_asset_is_assigned

не соответствуют именам файлов, которые вы перечисляете:

0003_auto__add_field_asset_is_reserved__add_field_asset_is_created__add_fie.py
0004_auto__del_field_asset_is_reserved__add_field_asset_is_assigned.py

Числа являются частью имени файла и должны полностью совпадать. Я не уверен, как вы достигли этого состояния, но если вы абсолютно уверены, что ваша БД соответствует тому, что находится в вашем файле 0004, вы можете добавить 0002_auto__chg_field_asset_username__chg_field_asset_title__chg_field_asset в таблицу южной БД, а затем обновить две строки, чтобы числа соответствовали вашим именам файлов.

Излишне говорить, что вы должны сделать резервную копию всего, прежде чем делать это.

person dgel    schedule 16.01.2012
comment
Похоже, что первый запуск миграции (0001initial) включает обе миграции 0001 и 0002, как описано в папке миграции. Это могло бы объяснить, почему номера миграции не синхронизировались. Явно ошибка в моем рабочем процессе. Значит, если я просто исправлю строки в базе данных, Юг снова синхронизируется, верно? Затем, после внесения этого исправления, должен ли я снова запустить миграцию, чтобы Юг перешел к миграции 0009 (именно это я и пытаюсь получить)? - person jchung; 16.01.2012
comment
Да, или migrate myapp --fake миграцию до точки, которую вы знаете, что ваша база данных в настоящее время представляет, и удалите фантомные миграции с аргументом --delete-ghost-migrations. - person Yuji 'Tomita' Tomita; 16.01.2012