Ponytech

Technology for ponies

Mar 31, 2013

Migrate your user profile data to Django 1.5 custom user model

One of the major feature of Django 1.5 is the new custom user model. This article is a step by step guide on how to migrate your existing user data using the legacy user profile (which will be dropped in version 1.7) to a custom user model without losing data in the process.

This guide will use the excellent South framework to handle the migration. If you are not already using South, then you should! But don't worry, it's not too late to convert your project to South.

Initial set up

Let's suppose we have an app called myapp and the following model which allows us to store user's date of birth along with the user model:

from django.db import models
from django.contrib.auth.models import User

class UserProfile(models.Model):
       user = models.ForeignKey(User, unique=True)
       birthdate = models.DateField('Date of birth', null=True)

And we use the deprecated user profile setting : AUTH_PROFILE_MODULE='myapp.UserProfile' . The aim of this article is to get rid of this extra model / table by integrating the birth date directly into the user model.

Convert project to South

First step is to convert the project to South. If you are already using South, you can skip to the next step. Install South with pip install south and add south to your INSTALLED_APPS. Converting your app is as easy as

./manage.py syncdb

./manage.py convert_to_south myapp

Prepare the migration

First create an empty MyUser model that ineherit from AbstractUser

from django.contrib.auth.models import AbstractUser
class MyUser(AbstractUser):
       pass

and create an empty schema migration :

./manage.py schemamigration myapp --empty rename_auth_user_to_myapp_my_user

This creates an empty schema migration file in myapp/migrations/ named 0002_rename_auth_user_to_myapp_my_user.py. Let's edit that file and just add rename instructions in order to easily maintain all our existing users:

def forwards(self, orm):
    db.rename_table('auth_user', 'myapp_myuser')

def backwards(self, orm):
    db.rename_table('myapp_myuser', 'auth_user')

Now we want that extra birthdate column for our user, so turn your empty MyUser model to:

class MyUser(AbstractUser):
       birthdate = models.DateField('Date of birth', null=True)

and make an auto migration to get the field added to the table:

./manage.py schemamigration myapp --auto

It is now time to actually copy the date of birth to the new model using a data migration. Start a new data migration with:

./manage.py datamigration myapp copy_birthdate

Edit the migration file 0004_copy_birthdate with:

def forwards(self, orm):
    for userprofile in orm['myapp.UserProfile'].objects.all():
        user = orm['myapp.MyUser'].objects.get(id=userprofile.user_id)
        user.birthdate = userprofile.birthdate
        user.save()

If you ever want to go back in your migrations list you have to copy birthdates from MyUser back to UserProfile in the backwards() method. I let you write this migration as an exercice!

Finally we can get rid of the UserProfile model / table. Remove this model from models.py and create one more auto schema migration:

./manage.py schemamigration myapp --auto

Migrate

To sum up you should have the following migration files in your migration folder:

  • 0001_initial.py
  • 0002_migrate_to_custom_user.py
  • 0003_auto__add_field_myuser_birthdate.py
  • 0004_copy_birthdate.py
  • 0005_auto__del_userprofile.py

Let's actually apply those migrations with:

./manage.py migrate myapp

And voila! You data had been converted to new custom user model. To make use of it remove AUTH_PROFILE_MODULE from your settings and replace it by AUTH_USER_MODEL = 'myapp.MyUser'.

For more info on how to use the cutom user model consult the django documentation.