South is more than able to do this migration for you, but you need to be smart and do it in stages. Here’s the step-by-step guide: (This guide presupposed you subclass AbstractUser
, not AbstractBaseUser
)
-
Before making the switch, make sure that south support is enabled in the application
that contains your custom user model (for the sake of the guide, we’ll call itaccounts
and the modelUser
).
At this point you should not yet have a custom user model.$ ./manage.py schemamigration accounts --initial Creating migrations directory at 'accounts/migrations'... Creating __init__.py in 'accounts/migrations'... Created 0001_initial.py. $ ./manage.py migrate accounts [--fake if you've already syncdb'd this app] Running migrations for accounts: - Migrating forwards to 0001_initial. > accounts:0001_initial - Loading initial data for accounts.
-
Create a new, blank user migration in the accounts app.
$ ./manage.py schemamigration accounts --empty switch_to_custom_user Created 0002_switch_to_custom_user.py.
-
Create your custom
User
model in theaccounts
app, but make sure it is defined as:class SiteUser(AbstractUser): pass
-
Fill in the blank migration with the following code.
# encoding: utf-8 from south.db import db from south.v2 import SchemaMigration class Migration(SchemaMigration): def forwards(self, orm): # Fill in the destination name with the table name of your model db.rename_table('auth_user', 'accounts_user') db.rename_table('auth_user_groups', 'accounts_user_groups') db.rename_table('auth_user_user_permissions', 'accounts_user_user_permissions') def backwards(self, orm): db.rename_table('accounts_user', 'auth_user') db.rename_table('accounts_user_groups', 'auth_user_groups') db.rename_table('accounts_user_user_permissions', 'auth_user_user_permissions') models = { ....... } # Leave this alone
-
Run the migration
$ ./manage.py migrate accounts - Migrating forwards to 0002_switch_to_custom_user. > accounts:0002_switch_to_custom_user - Loading initial data for accounts.
-
Make any changes to your user model now.
# settings.py AUTH_USER_MODEL = 'accounts.User' # accounts/models.py class SiteUser(AbstractUser): site = models.ForeignKey(Site, null=True)
-
create and run migrations for this change
$ ./manage.py schemamigration accounts --auto + Added field site on accounts.User Created 0003_auto__add_field_user_site.py. $ ./manage.py migrate accounts - Migrating forwards to 0003_auto__add_field_user_site. > accounts:0003_auto__add_field_user_site - Loading initial data for accounts.
Honestly, If you already have good knowledge of your setup and already use south, It should be as simple as adding the following migration to your accounts module.
# encoding: utf-8
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Fill in the destination name with the table name of your model
db.rename_table('auth_user', 'accounts_user')
db.rename_table('auth_user_groups', 'accounts_user_groups')
db.rename_table('auth_user_permissions', 'accounts_user_permissions')
# == YOUR CUSTOM COLUMNS ==
db.add_column('accounts_user', 'site_id',
models.ForeignKey(orm['sites.Site'], null=True, blank=False)))
def backwards(self, orm):
db.rename_table('accounts_user', 'auth_user')
db.rename_table('accounts_user_groups', 'auth_user_groups')
db.rename_table('accounts_user_user_permissions', 'auth_user_user_permissions')
# == YOUR CUSTOM COLUMNS ==
db.remove_column('accounts_user', 'site_id')
models = { ....... } # Leave this alone
EDIT 2/5/13: added rename for auth_user_group table. FKs will auto update to point at the correct table due to db constraints, but M2M fields’ table names are generated from the names of the 2 end tables and will need manual updating in this manner.
EDIT 2: Thanks to @Tuttle & @pix0r for the corrections.