Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Migrating existing auth.User data to new Django 1.5 custom user model?

I'd prefer not to destroy all the users on my site. But I want to take advantage of Django 1.5's custom pluggable user model. Here's my new user model:

class SiteUser(AbstractUser):     site = models.ForeignKey(Site, null=True) 

Everything works with my new model on a new install (I've got other code, along with a good reason for doing this--all of which are irrelevant here). But if I put this on my live site and syncdb & migrate, I'll lose all my users or at least they'll be in a different, orphaned table than the new table created for my new model.

I'm familiar with South, but based on this post and some trials on my part, it seems its data migrations are not currently a fit for this specific migration. So I'm looking for some way to either make South work for this or for some non-South migration (raw SQL, dumpdata/loaddata, or otherwise) that I can run on each of my servers (Postgres 9.2) to migrate the users once the new table has been created while the old auth.User table is still in the database.

like image 826
B Robster Avatar asked Feb 15 '13 21:02

B Robster


People also ask

What is difference between migrate and Makemigrations in Django?

migrate , which is responsible for applying and unapplying migrations. makemigrations , which is responsible for creating new migrations based on the changes you have made to your models.


1 Answers

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)

  1. 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 it accounts and the model User). 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. 
  2. 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. 
  3. Create your custom User model in the accounts app, but make sure it is defined as:

    class SiteUser(AbstractUser): pass 
  4. 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 
  5. 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. 
  6. 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) 
  7. 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.

like image 170
Thomas Avatar answered Sep 22 '22 11:09

Thomas