Overview
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 of user with the help of 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 an User object is created. Finally we will customize django admin.
Note: This should be done at the begining of the project before running any migrations.
Step 1
- Create an app.
python manage.py startapp users
- Now register your app in your installed apps.
# settings.py INSTALLED_APPS += [ 'users', ]
- Add AUTH_USER_MODEL in settings.py
# settings.py AUTH_USER_MODEL = 'users.User'
Step 2
- Create a file constants.py inside users 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
- Run the commands below to make changes
python manage.py makemigrations python manage.py migrate
Step 4
- Now let's create signals for automatically creating 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 import signals 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
- 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
- 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 6
- You have successfully extended the user model. Lets create an user
python manage.py createsuperuser #output Email: [email protected] Password: .......... Password (again): .......... Superuser created successfully
- You user should be created by now, login to your admin panel to see.