Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is a Django South GhostMigrations exception and how do you debug it?

Tags:

Made some changes to my Django app's model and used South to migrate them on my development machine (migrations 0004 through 0009). But when trying to migrate these changes on the server, I get a "GhostMigrations" error.

There isn't much good content explaining what a ghost migration is, or how to debug one. Google wasn't helpful on this one and the other SO questions mentioning ghost migrations don't cover this either (the most helpful question here was mostly about workflow). The helpful folks over in the django-south IRC had this to say about ghost migrations: "it means south's history (a table in the db) records two migrations that it thinks have been applied, but whose migration files it can't find". I'm trying to figure out now how to complete the debug.

Thanks in advance for the help.

Here's the error:

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

I was surprised to see that South complained about migrations 0002 and 0003, because I made those changes months ago. The changes I made earlier today were changes 0004 through 0009.

Here's my model:

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)

Here are the contents of the south migrations folder:

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

This is the 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)

This is the myapp_asset table as it currently stands:

                                   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

I can't figure out why django-south considers migrations 0002 and 0003 to be "Ghosts". Both of them are in the migrations folder, are listed as "applied" in the migrationtable, and the database seems to be consistent with the end-state after migration 0003.

(possible errors: the migrations folder was included in the git repo; migration 0002 created an attribute, and then 0003 renamed it)

like image 981
jchung Avatar asked Jan 16 '12 03:01

jchung


2 Answers

Somehow, your database has recorded migrations 0002 and 0003 which it can't find in your migrations folder.

Migration 0002 in your filesystem is 0002_auto__chg_field_asset_username__chg_field_asset_title__chg_field_asset.py while the in in the history table it's 0002_auto__add_field_asset_is_reserved__add_field_asset_is_created__add_fie.py

South must have been migrated when your migrations folder had different contents (perhaps in development?).

Based on what you're saying, it looks to me like your database reflects the state at migration 0004, so I'd run a python manage.py migrate myapp 0004 --fake --delete-ghost-migrations which will set the migration table at the point you added the is_assigned field, and you can happily apply migrations 0005+.

You'd know best which migration the current DB table should match though!

like image 160
Yuji 'Tomita' Tomita Avatar answered Oct 11 '22 07:10

Yuji 'Tomita' Tomita


They are considered ghost migrations because the names in the database:

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

don't match the filenames you list:

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

The numbers are part of the filename and must match perfectly. I'm not sure how you reached this state, but if you are absolutely sure that your DB matches what is in your 0004 file, you could add 0002_auto__chg_field_asset_username__chg_field_asset_title__chg_field_asset to the south DB table, then update the two rows so the numbers match your filenames.

Needless to say you should back everything up before doing this.

like image 24
dgel Avatar answered Oct 11 '22 08:10

dgel