Custom User Model in Django
In this tutorial, we will be learning how to create a custom user model or extending user model to add some fields and to grab more control over user model.
Django's built-in User model is great and allows us to start using it. For most of the cases, it will work fine but official Django documentation highly recommends using a custom user model.
Django gives some limited fields to store data but in most of the cases, we want to store few more user information it's challenging to customize existing user model that's why it's better to extend existing user model.
We can create a custom user model in two ways -
Extending AbstractUser class #
It is a good idea to extend to AbstractUser and add our fields to the existing model. So, all the basic fields will be by default available.
Set Up
Create a new directory and navigate into it and install django.
pip install django
Create a new project
django-admin startproject userproject
Navigate into to project folder then run the server to check everything is working or not.
python manage.py runserver
If everything works fine then open http://127.0.0.1:8000/ and you should get this
Action
Then start a new app
python manage.py startapp users
At this point your project structure should look like -
userproject/
|-- userproject/
| |-- __init__.py
| |-- asgi.py
| |-- settings.py
| |-- urls.py
| |-- wsgi.py
|-- users/
| |-- __init__.py
| |-- admin.py
| |-- apps.py
| |-- models.py
| |-- tests.py
| |-- views.py
|-- db.sqlite3
|-- manage.py
Then update settings.py file
# userproject/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users', # new
]
AUTH_USER_MODEL = 'users.CustomUser' # new
NOTE: Do not migrate the app. Because we want to change the user table and after migration normal user table will be created. Now update users/models.py with custom user model and with all the new fields required by extending the AbstractUser class
# users/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
address = models.TextField()
contact_no = models.CharField(max_length=10)
Now we have to create forms to create new user and modification of an existing user. Create a new file inside users app forms.py i.e., users/forms.py
# users/forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from users.models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = CustomUser
fields = UserCreationForm.Meta.fields + ('address', 'contact_no')
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = CustomUser
fields = UserChangeForm.Meta.fields
Here we are using model forms and setting model to our CustomUser model and all the default fields. By default, django provides these fields: username, first_name, last_name, email, password, groups etc. For more information check out: Django User Model description.
Now update admin.py
# users/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from users.forms import CustomUserCreationForm, CustomUserChangeForm
from users.models import CustomUser
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
admin.site.register(CustomUser, CustomUserAdmin)
Here, we have created an admin for custom user to show information on Django admin page.
Now make migrations
python manage.py makemigrations
After this, you may see this output for migration of our users model
Migrations for 'users':
users\migrations\0001_initial.py
- Create model CustomUser
And then migrate
python manage.py migrate
Thats it, we are done with Custom user model.
To ensure that everything is working fine create a superuser.
python manage.py createsuperuser
If you are able to create a superuser then everything is fine. Now we can use our Custom user model in our application.
Extending AbstractBaseUser class #
If we try to create a custom user by extending AbstractBaseUser and basically we need to rewrite Django. It can be useful for a completely customized application like email instead of a username for login.
Set Up
Create a new directory and navigate into it and install django.
pip install django
Create a new project
django-admin startproject userproject
Navigate into to project folder then run the server to check everything is working or not.
python manage.py runserver
If everything works fine then open http://127.0.0.1:8000/ and you should get this
Action
Then start a new app
python manage.py startapp users
At this point your project structure should look like -
userproject/
|-- userproject/
| |-- __init__.py
| |-- asgi.py
| |-- settings.py
| |-- urls.py
| |-- wsgi.py
|-- users/
| |-- __init__.py
| |-- admin.py
| |-- apps.py
| |-- models.py
| |-- tests.py
| |-- views.py
|-- db.sqlite3
|-- manage.py
Then update settings.py file
# userproject/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users', # new
]
AUTH_USER_MODEL = 'users.User' # new
NOTE: Do not migrate the app. Because we want to change the user table and after migration normal user table will be created. Now update
users/models.py
with custom user model and with all the new fields required by extending the AbstractBaseUser class
# users/models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
class User(AbstractBaseUser):
# add all the required fields
email = models.EmailField(max_length=255, unique=True)
first_name = models.CharField(max_length=30, blank=True)
last_name = models.CharField(max_length=30, blank=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def __str__(self):
return self.email
def get_full_name(self):
full_name = '%s %s' % (self.first_name, self.last_name)
return full_name.strip()
def get_short_name(self):
return self.first_name.strip()
@property
def is_staff(self):
return self.is_admin
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
- USERNAME_FIELD: Whatever field we mention here will be treated as main field to login and logout user. It means by default username field is prompted during but now we have to input email during login.
- REQUIRED_FIELDS: Fields which are added here will be aksed to user during creation of superuser if they use
python manage.py createsuperuser
command. Here, we are keeping it blank because by default the feild mentioned asUSERNAME_FIELD
and password is present here.
Then create a new file managers.py
inside users app i.e., users/managers.py
Now users directory should look like -
users/
|-- __init__.py
|-- admin.py
|-- apps.py
|-- managers.py
|-- models.py
|-- tests.py
`-- views.py
Now create a UserManager class inside managers.py
file and define create_user and create_superuser methods.
# users/managers.py
from django.contrib.auth.models import BaseUserManager
class UserManager(BaseUserManager):
def create_user(self, email, password=None):
if not email:
raise ValueError('Users must have an email address')
if not password:
raise ValueError('Users must have a password')
user = self.model(
email=self.normalize_email(email),
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password):
user = self.create_user(
email,
password=password,
)
user.is_admin = True
user.save(using=self._db)
return user
Now import UserManager from managers.py
and add it to User model class. Add anywhere inside User class.
# users/models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
from users.managers import UserManager # new
class User(AbstractBaseUser):
...
objects = UserManager() # new
...
Now we have to create forms for creating and updating users. Create a new file forms.py inside users app directory users/forms.py
# users/forms.py
from django import forms
from django.contrib import admin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from users.models import User
class UserCreationForm(forms.ModelForm):
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(
label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = User
fields = ('email', 'first_name', 'last_name')
def clean_password2(self):
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Password don't match")
return password2
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data['password1'])
if commit:
user.save()
return user
class UserChangeForm(forms.ModelForm):
password = ReadOnlyPasswordHashField()
class Meta:
model = User
fields = ('email', 'password', 'first_name', 'last_name', 'is_active', 'is_admin')
def clean_password(self):
return self.initial['password']
Then register all the required data into admin.py
# users/admin.py
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import Group
from django.contrib import admin
from users.forms import UserChangeForm, UserCreationForm
from users.models import User
class UserAdmin(BaseUserAdmin):
form = UserChangeForm
add_form = UserCreationForm
list_display = ('email', 'is_admin')
list_filter = ('is_admin',)
fieldsets = (
(None, {'fields': ('email', 'password', 'first_name', 'last_name',)}),
('Permissions', {'fields': ('is_admin',)}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2'),
}),
)
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ()
class Meta:
model = User
admin.site.register(User, UserAdmin)
admin.site.unregister(Group)
Now make migrations
python manage.py makemigrations
After this, you may see this output for migration of our users model
Migrations for 'users':
users\migrations\0001_initial.py
- Create model User
And then migrate
python manage.py migrate
Thats it, we are done with Custom user model. Now try to create a superuser you will be prompted for email instead if username.
python manage.py createsuperuser
By following these steps we can create our custom user model with email as primary field.