Kaizen Today I Learned by Ville Säävuori

Migrating to a Custom Django User Model Mid-Project

Using a custom user model in a Django project is almost always a good idea. The documentation for adding one when starting a project is good and clear but switching to a custom model mid-project is not that easy. This blog post explains one way, and ticket #25313 has several helpful comments.

The following steps based on comment #18 worked for me:

Stage 1, in development

  1. Create new app (or use existing one that has no migrations yet) for the new user model
  2. Create a user model that is identical to the auth.User:
class User(AbstractUser):
    class Meta:
        db_table = "auth_user"
  1. Add the new user model app to INSTALLED_APPS and add the new model as AUTH_USER_MODEL
  2. Replace all occurences of from django.contrib.auth.models import User with from newusermodelapp.models import User (Note: the documentation recommends using django.contrib.auth.get_user_model() instead but in practise the user model almost never changes to in my experience using normal module import is simpler and works better. YMMW)
  3. Delete all old migrations (NOTE: do NOT run following in a root that includes virtualenv folder):
find . -path "*/migrations/*.py" -not -name "__init__.py" -delete
find . -path "*/migrations/*.pyc" -delete
  1. Create new migrations from scratch: manage.py makemigrations
  2. The tests should pass now and the project should run normally. The last step is to manually reset the migrations table in the database by running TRUNCATE TABLE django_migrations; in the db shell and then finally run all migrations with a fake flag: manage.py migrate --fake
  3. Push the tested code for running the first part in production.

Stage 2, in production

  1. Pull new code, update requirements
  2. Reset the migration table TRUNCATE TABLE django_migrations; and then fake all the migrations manage.py migrate --fake

Stage 3, in development

  1. Now remove the db_table = "auth_user" from the custom user model, create migrations
  2. Apply the migrations manage.py migrate
  3. Lastly, the content types are now mixed up and they need fixing one way or another. An easy way is to execute the following SQL (example in PostgreSQL):
UPDATE django_content_type SET app_label = 'nonexistent' WHERE app_label = 'newusermodelapp' and model = 'user';
UPDATE django_content_type SET app_label = 'newusermodelapp' WHERE app_label = 'users' and model = 'user';
  1. Make sure the tests pass, then commit and push the code

Stage 4, in production

Pull the new code, then execute steps 2 and 3 from the previous stage in production.

Now you have a custom user model in a fresh state an you can modify the model as you need using a normal development process.

Final notes

Splitting the process in to smaller steps may or may not work for your project. If the production database is small enough, an easy way to simplify this process would be to copy the production database in to development environment, run all the steps in dev, and finally import the modified database back to production.

Finally, in a somewhat related note to self; always start new projects with a custom user model to avoid this kind of unnecessary mess!

Tagged with , ,

Published . Last modified .