One of the first things I do after creating a new Django or Django Rest Framework project is to create a custom User model. Part 1 deals with case when you add custom User model right at the start of a project. In part 2, we deal with scenario when custom User model is added later in the project.
What exactly is a User model for?
Django uses a User model for only one purpose, user authentication. If the project is really small and not ever going to scale, you can tack on non-authentication related stuff as well, like user address, user profile data etc but this is not recommended. I like to keep my models simple and they only contain what is necessary for authentication. Everything else, I move it to other models like UserProfile etc
Why custom user model?
Well, Django’s default created User model makes certain crippling assumptions. Like it will only use username for login. If you already put your project in production with active users and then down the line wanted to change it to use email for authentication, this will not be easy. If the active users are too many, it might even be impossible to change. It is so because it involves deleting the old database models and create new ones that use the new user model. So, we must create a custom user model right at the start and switch over to email for authentication. This also makes it easy to add other customization later on. Do this before doing your first migration.
Create Custom User Model
Follow these steps.
- create a new app, only for managing custom user model. I call it
accountsin most of my projects.
- create User Model and Model Manager. In my case, I called the model as
MzkUserand model manager as
MzkUserManager. Note, it is important to NOT name them as User and UserManager as these might become reserved words in future. Since my project was about a music app, I prepended it with Mzk. In the code below, all models and model-methods and properties are required.
accounts/models.pyPython123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081from django.db import modelsfrom django.contrib.auth.models import (AbstractBaseUser, BaseUserManager)class MzkUserManager(BaseUserManager):# create_user takes all REQUIRED_FIELDS and USERNAME_FIELDdef create_user(self, email, password=None, is_active=True, is_staff=False, is_admin=False):if not email:raise ValueError("Users must have an email address")if not password:raise ValueError("Users must have a password")user_obj = self.model(email = self.normalize_email(email))user_obj.set_password(password)user_obj.staff = is_staffuser_obj.admin = is_adminuser_obj.active = is_activeuser_obj.save(using=self._db)return user_objdef create_staff_user(self, email, password=None):user = self.create_user(email,password=password,is_staff=True)return userdef create_superuser(self, email, password=None):user = self.create_user(email,password=password,is_staff=True,is_admin=True)return user# password is inherited from AbstractBaseUserclass MzkUser(AbstractBaseUser):email = models.EmailField(email@example.com', max_length=255, unique=True)active = models.BooleanField(default=True)staff = models.BooleanField(default=False)admin = models.BooleanField(default=False)timestamp = models.DateTimeField(auto_now_add=True)USERNAME_FIELD = 'email'REQUIRED_FIELDS = objects = MzkUserManager()def __str__(self):return self.emaildef get_full_name(self):return self.emaildef get_short_name(self):return self.email# we don't use object/module level permissionsdef has_perm(self, perm, obj=None):return True# we don't use module level permissionsdef has_module_perms(self, app_label):return True@propertydef is_staff(self):return self.staff@propertydef is_admin(self):return self.admin@propertydef is_active(self):return self.active
- settings.py: Register the
accountsapp by adding it to
INSTALLED_APPS. Then add the following line:
AUTH_USER_MODEL = 'accounts.MzkUser'
This will indicate which custom user model we are using
- Run makemigrations and migrate
- accounts/admin.py: The above steps will enable the project to use custom user model. But if we went to the admin page, this will still not be visible (admin will use this model to add new users). To be able to see this on admin page, we need to also register this model with admin panel like so:
accounts/admin.pyPython123456789101112from django.contrib import adminfrom django.contrib.auth import get_user_modelUser = get_user_model()class UserAdmin(admin.ModelAdmin):class Meta:model = Useradmin.site.register(User)
- Now run migrations, create superuser and then goto admin page to see the new user model.
Note: Here, I am not using object-level permissions. Hence am returning
has_module_perms method. You will anyways want to use a library like
django-guardian for managing object-level permissions.
You will also want to revisit
has_perm method when you implement user level permissions. For simplicity, I am returning