什么是 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,在迁移文件夹中找不到它们。
您的文件系统中的迁移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
当您的迁移文件夹具有不同的内容时(可能正在开发中?),肯定已经迁移了 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.contrib 中? [复制]
为啥 Django South 1.0 使用 iteritems()?