什么是 Django South GhostMigrations 异常以及如何调试它?

Posted

技术标签:

【中文标题】什么是 Django South GhostMigrations 异常以及如何调试它?【英文标题】:What is a Django South GhostMigrations exception and how do you debug it? 【发布时间】:2012-02-11 03:00:49 【问题描述】:

对我的 Django 应用模型进行了一些更改,并使用 South 在我的开发机器上迁移它们(迁移 0004 到 0009)。但是当尝试在服务器上迁移这些更改时,我收到“GhostMigrations”错误。

没有太多好的内容可以解释什么是幽灵迁移,或者如何调试。谷歌在这个问题上没有帮助,其他提到幽灵迁移的 SO 问题也没有涵盖这一点(最有帮助的问题 here 主要是关于工作流程的)。 django-south IRC 中的乐于助人的人对幽灵迁移有这样的说法:“这意味着南的历史(数据库中的一个表)记录了它认为已应用的两次迁移,但找不到其迁移文件” .我现在想弄清楚如何完成调试。

提前感谢您的帮助。

错误如下:

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

看到 South 抱怨迁移 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 是“幽灵”。两者都在migrations文件夹中,在migrationtable中被列为“applied”,数据库似乎与migration 0003后的end-state一致。

(可能的错误:迁移文件夹包含在 git repo 中;迁移 0002 创建了一个属性,然后 0003 将其重命名)

【问题讨论】:

【参考方案1】:

它们被认为是幽灵迁移,因为数据库中的名称:

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 添加到南数据库表中,然后更新两行以使数字与您的匹配文件名。

不用说您应该在执行此操作之前备份所有内容。

【讨论】:

看起来第一次迁移运行 (0001initial) 包括迁移 0001 和 0002,如迁移文件夹中所述。这可以解释为什么迁移数字不同步。显然是我的工作流程中的一个错误。因此,如果我只是修复数据库中的行,那将使 South 重新同步,对吗?然后在修复之后,我会再次运行“迁移”以让 South 继续迁移到迁移 0009(这是我想要得到的地方)? 是的,或者 migrate myapp --fake 迁移到您知道您的数据库当前代表的点,并使用 --delete-ghost-migrations 参数删除幽灵迁移。【参考方案2】:

不知何故,您的数据库记录了迁移 0002 和 0003,在迁移文件夹中找不到它们。

您的文件系统中的迁移00020002_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

当您的迁移文件夹具有不同的内容时(可能正在开发中?),肯定已经迁移了 South。

根据您所说的,在我看来您的数据库反映了迁移0004 时的状态,因此我将运行python manage.py migrate myapp 0004 --fake --delete-ghost-migrations,它将在您添加@987654326 时设置迁移表@field,你可以愉快地应用迁移0005+

您最清楚当前数据库表应该匹配哪个迁移!

【讨论】:

谢谢宇治。快速提问:--delete-ghost-migrations 中究竟发生了什么? South 是否只是从 south_migrationtable 中删除这些行并用正确的行替换它们?我问是因为在迁移 0002 和 0003 中创建的字段已经被填充。 South 不会删除这些数据并重新创建它,对吗? @Jared,不,它只会修改south_migrationhistory 表。您仍然需要指定 --fake,因为 south 将无法应用“真正的”迁移 2、3 和 4,因为您的数据库已经反映了这些更改(或者甚至更多 - 但我无法从您的代码示例中看出单独)

以上是关于什么是 Django South GhostMigrations 异常以及如何调试它?的主要内容,如果未能解决你的问题,请参考以下文章

不知道为啥 django South 试图运行反向迁移

为啥 django-south 不包含在 django.contrib 中? [复制]

为啥 Django South 1.0 使用 iteritems()?

运行单元测试时禁用 Django South?

Django South:结合“alter table”以获得更好的性能?

无法在 Django 1.7 中创建 South 数据库模型