In general, Django build-in user models are great. but in the large and scale, web applications need to think out-of-box. So there are needs, different user models, with custom more new model fields. Now how can we extend Django user models new fields? Solutions are, Django abstract base user. Another great thing Django gives us build-in abstract user models.
In this article, I will make Django abstract user model with custom fields. I will give also an example of advance user admin separation with an abstract model
Django provides us, two different abstractuser models…
- AbstractUser
AbstractUser models are complete with base user fields and full feature with admin-compliant permissions - AbstractBaseUser
AbstractBaseUser models are pretty clean with password, last_login, and is_active fields.
I am gonna using “AbstractBaseUser” for with clean user model
Here is an abstract base user model
Create account app in django project ***
You can also add Django user permission mixin, it’s your web application requirements. Keep in mind it’s not required.
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin class User(AbstractBaseUser): """ Custom abstract user Model. """ # Names first_name = models.CharField(max_length=15, blank=True, null=True) last_name = models.CharField(max_length=15, blank=True, null=True) username = models.CharField(max_length=30, blank=True, null=True) # contact email = models.EmailField(unique=True) # require number = models.PositiveIntegerField(blank=True, null=True) website = models.URLField(blank=True, null=True) # about avatar = models.ImageField(upload_to='user/avatar/', blank=True, null=True) # Registration created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) # Permission is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) is_admin = models.BooleanField(default=False) # Main Field for authentication USERNAME_FIELD = 'email' # When user create must need to fill this field REQUIRED_FIELDS = ["number"] objects = UserManager() def __str__(self): return self.email class Meta: ordering = ('-created_at', '-updated_at', ) def get_full_name(self): if self.first_name: return f'{self.first_name} {self.last_name}' return self.email.split('@')[0] def has_perm(self, perm, obj=None): return True def has_module_perms(self, app_label): return True
Description: you can see “USERNAME_FIELD = ’email'”. it’s change authenticated username fields to email. and also see “REQUIRED_FIELDS = [“number”]”, it’s mean number fields is require to create user instance.
Abstract base user Model Manager
For django abstract base user model manager required build-in BaseUserManager inheritance. let’s example:
Make user UserManager listed bottom of user models
from django.contrib.auth.models import BaseUserManager class UserManager(BaseUserManager): """ User Model Manager """ def create_user(self, email, password=None, is_staff=False, is_admin=False, is_active=True): if not email: raise ValueError('Users must have email Address') if not password: raise ValueError('User must have Password') # if not full_name: # raise ValueError('User must have a full name') user_obj = self.model( email=self.normalize_email(email), ) user_obj.set_password(password) user_obj.is_staff = is_staff user_obj.is_admin = is_admin user_obj.is_active = is_active user_obj.save(using=self._db) def create_staffuser(self, email, password=None): user = self.create_user( email, password=password, is_staff=True ) return user def create_superuser(self, email, password=None): user = self.create_user( email, password=password, is_staff=True, is_admin=True ) return user ## User model here
Configration django Settings file *
Change AUTH_USER_MODEL with new user models
AUTH_USER_MODEL = 'account.User'
DJango admin create and update forms for new user model
from django import forms from django.utils.translation import gettext_lazy as _ from django.contrib.auth import get_user_model from django.contrib.auth.forms import ReadOnlyPasswordHashField User = get_user_model() class UserAdminCreationForm(forms.ModelForm): """A form for creating new users in django admin. 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 = User 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(UserAdminCreationForm, self).save(commit=False) user.set_password(self.cleaned_data["password1"]) if commit: user.save() return user class UserAdminChangeForm(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 = User fields = ['email', 'password', 'is_active', 'is_staff', 'is_admin'] 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"]
Add abstract base user in admin
In admin.py file, Make sure imported UserAdmin build-in class not conflict with your new class.
from django.contrib import admin from django.contrib.auth.admin import UserAdmin from django.contrib.auth import get_user_model from .models import UserShippingAddressGroup from .forms import UserAdminChangeForm, UserAdminCreationForm User = get_user_model() @admin.register(User) class UserInAdmin(UserAdmin): """ All User Admin Model (Include Super User) """ # The forms to add and change user instances form = UserAdminChangeForm add_form = UserAdminCreationForm search_fields = ['email', 'first_name', 'last_name', 'is_admin', 'is_staff', 'is_active'] list_display = ['email', 'is_admin', 'is_staff', 'is_active'] list_filter = ['is_admin', 'is_staff', 'is_active'] readonly_fields = ('created_at', 'updated_at', 'last_login') inlines = [UserCompanyInline] fieldsets = ( (None, {'fields': ('email', 'password', ('first_name', 'last_name'),)}), ('Contact', { # 'classes': ('collapse',), 'fields': ('number', 'website',) }), ('Biographical Details', { # 'classes': ('collapse',), 'fields': ('avatar',) }), ('Permissions', {'fields': ('is_admin', 'is_staff', 'is_active')}), # ('Group Permissions', {'fields': ('groups', 'user_permissions')}), if add permissions class ('Time', {'fields': ('last_login', 'created_at', 'updated_at')}), ) add_fieldsets = ( (None, { # 'classes': ('wide',), 'fields': ('first_name', 'last_name', 'email', 'password1', 'password2')} ), ) ordering = ('email',) filter_horizontal = ()
Test Now in admin
- Add user
- Edit User
Conclusion
We know django is great framework. we can make same thing with differnt way. it’s all how i use django abstract base user for highly customized scaleble user model.
Feel free contact me if you face any problem. Or please comment you problem
Thanks you.