Tech Incent
Django

Django abstractuser with example

django-abstractbaseuser

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…

  1. AbstractUser
    AbstractUser models are complete with base user fields and full feature with admin-compliant permissions
  2. 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

  1. Add user
    addnewuser-abstractuser
  2. Edit User
    edituser-abstractbaseuser

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.

Related posts

How to deploy django and react in heroku ?

Tech Incent

Explained Django inline formset factory with example?

Sajal Mia

How to create Django form?

Sajal Mia

Understanding Django and django design principles

Tech Incent

How to create a virtual environment for Django?

Sajal Mia

How to setup django static and media file in aws s3 ?

Sajal Mia