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
- Create new app (or use existing one that has no migrations yet) for the new user model
- Create a user model that is identical to the auth.User:
class User(AbstractUser): class Meta: db_table = "auth_user"
- Add the new user model app to
INSTALLED_APPSand add the new model as
- Replace all occurences of
from django.contrib.auth.models import Userwith
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)
- 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
- Create new migrations from scratch:
- 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
- Push the tested code for running the first part in production.
Stage 2, in production
- Pull new code, update requirements
- Reset the migration table
TRUNCATE TABLE django_migrations;and then fake all the migrations
manage.py migrate --fake
Stage 3, in development
- Now remove the
db_table = "auth_user"from the custom user model, create migrations
- Apply the migrations
- 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';
- 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.
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!