Ticket #235 (closed defect: fixed)
adding unique=True to a field that formerly had db_index=True, autodetection fails
| Reported by: | carl@… | Owned by: | andrew |
|---|---|---|---|
| Priority: | major | Milestone: | 0.7 |
| Component: | migrations | Version: | 0.6 |
| Keywords: | Cc: |
Description
Create a model like this (in myapp/models.py):
class TestModel(models.Model):
slug = models.SlugField()
Perform its initial migration. Then add unique=True:
class TestModel(models.Model):
slug = models.SlugField(unique=True)
Auto-create a migration:
$ ./manage.py startmigration myapp --auto unique_slug
And run the migration:
$ ./manage.py migrate myapp
Running migrations for myapp:
- Migrating forwards to 0002_unique_slug.
> myapp: 0002_unique_slug
Traceback (most recent call last):
File "/home/carljm/projects/testproj_env/bin/django-admin.py", line 7, in <module>
execfile(__file__)
File "/home/carljm/projects/testproj_env/src/django/django/bin/django-admin.py", line 5, in <module>
management.execute_from_command_line()
File "/home/carljm/projects/testproj_env/src/django/django/core/management/__init__.py", line 353, in execute_from_command_line
utility.execute()
File "/home/carljm/projects/testproj_env/src/django/django/core/management/__init__.py", line 303, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/home/carljm/projects/testproj_env/src/django/django/core/management/base.py", line 195, in run_from_argv
self.execute(*args, **options.__dict__)
File "/home/carljm/projects/testproj_env/src/django/django/core/management/base.py", line 222, in execute
output = self.handle(*args, **options)
File "/home/carljm/projects/testproj_env/src/south/south/management/commands/migrate.py", line 91, in handle
skip = skip,
File "/home/carljm/projects/testproj_env/src/south/south/migration.py", line 581, in migrate_app
result = run_forwards(mapp, [mname], fake=fake, db_dry_run=db_dry_run, verbosity=verbosity)
File "/home/carljm/projects/testproj_env/src/south/south/migration.py", line 388, in run_forwards
verbosity = verbosity,
File "/home/carljm/projects/testproj_env/src/south/south/migration.py", line 329, in run_migrations
runfunc(orm)
File "/home/carljm/projects/testproj/content/migrations/0002_unique_slug.py", line 11, in forwards
db.create_unique('myapp_testmodel', ['slug'])
File "/home/carljm/projects/testproj_env/src/south/south/db/generic.py", line 358, in create_unique
self.execute("ALTER TABLE %s ADD CONSTRAINT %s UNIQUE (%s)" % (qn(table_name), qn(name), cols))
File "/home/carljm/projects/testproj_env/src/south/south/db/generic.py", line 82, in execute
cursor.execute(sql, params)
File "/home/carljm/projects/testproj_env/src/django/django/db/backends/util.py", line 19, in execute
return self.cursor.execute(sql, params)
psycopg2.ProgrammingError: relation "myapp_testmodel_slug" already exists
The problem is that an index myapp_testmodel_slug already existed for the db_index=True (which SlugFields? have on by default), and the generated migration tries to create a new unique index with the same name, without deleting the old one.
The manual fix is to add
db.delete_index('myapp_testmodel', ['slug'])
to the beginning of the migration's "forwards" method (before the new unique index is created), and likewise
db.create_index('myapp_testmodel', ['slug'])
to the end of the "backwards" method. But it would be nice if South figured this out itself.
(The problem is the same if the field is not a SlugField? and db_index=True is explicitly specified in the first version of the model.)

Yes, this is the old problem about indexes, uniques, MySQL, and various other things like that. I'm scheduling this for 0.7 to fix it, since there's thankfully a workaround.