While working on some Django project you might feel that the default user model is not fulfilling all the requirements.
For example, you may want to authenticate users by email Id and not by username. You may want to store some more extra information in the user model. In short, you might want to define your own custom user model.
In particular, we may encounter one out of below two scenarios:
We will discuss the second approach here. Our approach will use custom-defined user model and custom define backend authentication mechanism.
First of all, create the user model class in your models directory. Add the usermanager class also.
from django.db import models from django.contrib.auth.models import AbstractBaseUser, BaseUserManager class MyUserManager(BaseUserManager): use_in_migrations = True # python manage.py createsuperuser def create_superuser(self, email, is_staff, password): user = self.model( email = email, is_staff = is_staff, ) user.set_password(password) user.save(using=self._db) return user class UserModel(AbstractBaseUser): sys_id = models.AutoField(primary_key=True, blank=True) email = models.EmailField(max_length=127, unique=True, null=False, blank=False) is_staff = models.BooleanField() is_active = models.BooleanField(default=True) objects = MyUserManager() USERNAME_FIELD = "email" # REQUIRED_FIELDS must contain all required fields on your User model, # but should not contain the USERNAME_FIELD or password as these fields will always be prompted for. REQUIRED_FIELDS = ['is_staff'] class Meta: app_label = "accounts" db_table = "users" def __str__(self): return self.email def get_full_name(self): return self.email def get_short_name(self): return self.email # this methods are require to login super user from admin panel def has_perm(self, perm, obj=None): return self.is_staff # this methods are require to login super user from admin panel def has_module_perms(self, app_label): return self.is_staff
email
, password
, is_active
and is_staff
, I removed everything else from the model. Is_staff
is required for user login in the admin panel. If you are not going to use admin site then is_staff can be removed as well.Define this newly created custom user model in the settings file. The project should know that we are going to use other than the default user model.
AUTH_USER_MODEL = 'accounts.UserModel' AUTHENTICATION_BACKENDS = ('accounts.backends.MyAuthBackend','django.contrib.auth.backends.ModelBackend',)
As shown in the code above, mention the name of custom authentication backend we will create to support our custom user model.
AUTHENTICATION_BACKENDS is a list of backends. If the first one fails to authenticate, the second is used as a fallback. Here first authentication backend in the list is the one we will create and second is the Django's default authentication backend.
Create the custom authentication backend.
backends.py :
from accounts.models import UserModel import logging class MyAuthBackend(object): def authenticate(self, email, password): try: user = UserModel.objects.get(email=email) if user.check_password(password): return user else: return None except UserModel.DoesNotExist: logging.getLogger("error_logger").error("user with login %s does not exists " % login) return None except Exception as e: logging.getLogger("error_logger").error(repr(e)) return None def get_user(self, user_id): try: user = UserModel.objects.get(sys_id=user_id) if user.is_active: return user return None except UserModel.DoesNotExist: logging.getLogger("error_logger").error("user with %(user_id)d not found") return None
Now run makemigrations
and migrations
command. This will create the custom user model in the database. (Assuming database settings are ok).
$ python manage.py makemigrations
$ python manage.py migrate
Now create a super user. is_staff should be true for this user.
$ python manage.py createsuperuser
Running this command will ask you email id, is_staff, password and confirm password.
After the user is created successfully, go to admin login and confirm if you are able to log in.
If you are getting error Please enter the correct email and password for a staff account. Note that both fields may be case-sensitive, make sure you have is_staff field in the model and has_perm and pas_module_perm methods define and they return true.
If you are able to login successfully, Good.
In the above section, we created a custom user model with them help of AbstractBaseUser
. Custom user models created by this method are not registered normally with the admin site. For this, we need to override Django's default UserAdmin.
In the app directory, create a file admin.py
if the file does not exist already.
from django import forms from django.contrib import admin from django.contrib.auth.models import Group from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from django.contrib.auth.forms import ReadOnlyPasswordHashField from accounts.models import UserModel class UserCreationForm(forms.ModelForm): #A form for creating new users. Includes all the required #fields, plus a repeated password. password1 = forms.CharField(label='Password', widget=forms.PasswordInput) password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput) class Meta: model = UserModel fields = ('email') def clean_password2(self): # Check that the two password entries match password1 = self.cleaned_data.get("password1") password2 = self.cleaned_data.get("password2") if password1 and password2 and password1 != password2: raise forms.ValidationError("Passwords don't match") return password2 def save(self, commit=True): # Save the provided password in hashed format user = super(UserCreationForm, self).save(commit=False) user.set_password(self.cleaned_data["password1"]) if commit: user.save() return user class UserChangeForm(forms.ModelForm): #A form for updating users. Includes all the fields on #the user, but replaces the password field with admin's #password hash display field. password = ReadOnlyPasswordHashField() class Meta: model = UserModel fields = ('email', 'password', 'is_active','is_staff') def clean_password(self): # Regardless of what the user provides, return the initial value. # This is done here, rather than on the field, because the # field does not have access to the initial value return self.initial["password"] class UserAdmin(BaseUserAdmin): # The forms to add and change user instances form = UserChangeForm add_form = UserCreationForm # The fields to be used in displaying the User model. # These override the definitions on the base UserAdmin # that reference specific fields on auth.User. list_display = ('email', 'is_active', 'is_staff') list_filter = ('email',) fieldsets = ( (None, {'fields': ('email', 'password')}), ('Permissions', {'fields': ('is_staff',)}), ) # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin # overrides get_fieldsets to use this attribute when creating a user. add_fieldsets = ( (None, { 'classes': ('wide',), 'fields': ('email','password1', 'password2')} ), ) search_fields = ('email',) ordering = ('email',) filter_horizontal = () # Now register the new UserAdmin... admin.site.register(UserModel, UserAdmin) # ... and, since we're not using Django's built-in permissions, # unregister the Group model from admin. admin.site.unregister(Group)
Now restart the server and login to the admin site. You will be able to see the users model and you will be able to add, edit and delete the model objects.