Django ships with a built-in User model for authentication, however, the official Django documentation highly recommends using a custom user model for new projects.
There are two modern ways to create a custom user model in Django: AbstractUser and AbstractBaseUser. In both cases, we can subclass them to extend existing functionality.
However, AbstractBaseUser requires much, much more work. So here we will use AbstractUser.
We will also create a UserProfile model to store additional information about users with the help of a One-To-One Link. After this, we will extend the BaseUserManager functionality a little bit. We will also configure signals to automatically create UserProfile whenever a User object is created. Finally, we will customize the Django admin.
Note: This should be done at the begining of the project before running any migrations.
Step 1: New app
Create app users using the command
python
manage.py
startapp users
Now register your app in your installed apps in settings.py:
INSTALLED_APPS += [ 'users', ]
Set AUTH_USER_MODEL in settings.py to the new user model:
AUTH_USER_MODEL = 'users.User'
Step 2: Extend model and manager
Create a file constants.py inside users app directory
# users/constants.py
SUPERUSER = 1
STAFF = 2
STUDENT = 3
TUTOR = 4
USER_TYPE_CHOICES = (
(SUPERUSER, 'superuser'),
(STAFF, 'staff'),
(STUDENT, 'student'),
(TUTOR, 'tutor'),
)
Extend the User model in your users/models.py
# users/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils import timezone
from apps.users.managers import UserManager
from . import constants as user_constants
class User(AbstractUser):
username = None # remove username field, we will use email as unique identifier
email = models.EmailField(unique=True, null=True, db_index=True)
is_active = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
date_joined = models.DateTimeField(default=timezone.now)
user_type = models.PositiveSmallIntegerField(choices=user_constants.USER_TYPE_CHOICES)
REQUIRED_FIELDS = []
USERNAME_FIELD = 'email'
objects = UserManager()
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True,related_name="user_profile")
phone = models.CharField(max_length=255,blank=True,null=True)
is_verified = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.user.email
Extend UserManager in users/managers.py
# users/managers.py
from django.contrib.auth.models import BaseUserManager
from . import constants as user_constants
class UserManager(BaseUserManager):
def create_user(self, email, password, **extra_fields):
if not email:
raise ValueError('The Email must be set')
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_active', True)
extra_fields.setdefault('user_type', user_constants.SUPERUSER)
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self.create_user(email, password, **extra_fields)
Step 3: Migrations
Run the commands below to make database changes
python manage.py makemigrations
python manage.py migrate
Step 4: Create Signal
Now let's create signals for automatically creating a UserProfile instance in users/signals.py
# users/signals.py
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import UserProfile, User
@receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
instance.user_profile.save()
Now, register your signals file inside users/apps.py by adding a method ready and importing the signals file path.
# users/apps.py
from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'apps.users'
def ready(self):
import apps.users.signals
Now, add default_app_config to users/init.py
# users/__init__.py
default_app_config = 'users.apps.UsersConfig'
Step 5: Extend the admin form
Let's extend UserCreationForm and UserChangeForm in users/forms.py
# users/forms.py
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import User
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm):
model = User
fields = ('email',)
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = User
fields = ('email',)
Step 6: Customize Django user admin
Let's customize Django's admin now
# users/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import UserProfile, User
from .forms import CustomUserCreationForm, CustomUserChangeForm
class UserProfileInline(admin.StackedInline):
model = UserProfile
can_delete=False
verbose_plural_name="User Profile"
fk_name = 'user'
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = User
list_display_links = ['email']
search_fields = ('email',)
ordering = ('email',)
# inlines = (UserProfileInline,)
list_display = ('email', 'is_staff', 'is_active', 'is_superuser',)
list_filter = ('email', 'is_staff', 'is_active', 'is_superuser', 'user_type')
fieldsets = (
(None, {'fields': ('username', 'password')}),
(_('Personal info'), {'fields': ('first_name', 'last_name', 'email','user_type')}),
(_('Permissions'), {
'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions'),
}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2', 'is_staff', 'is_active', 'user_type')}
),
)
def get_inline_instances(self, request, obj=None):
if not obj:
return list()
return super(CustomUserAdmin, self).get_inline_instances(request, obj)
admin.site.register(User, CustomUserAdmin)
Step 7: Verifying
You have successfully extended the user model. Let's create a user
python manage.py createsuperuser
#output
Email: develop@raturi.in
Password: ..........
Password (again): ..........
Superuser created successfully
Your user should be created by now, login in to your admin panel to see.
That's all.