My Django custom user model I followed a video on YouTube to create a custom user model, this is my attempt:

# Django Imports
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.db import models

# The Custom (UserManager) Class
class UserManager(BaseUserManager):
    # Defining A Function That Will Be Used To Create New Users
    def create_user(self, email, username, f_name, l_name, password = None):
        if not email:
            raise ValueError('Users Must Have An E-Mail Address')
        if not username:
            raise ValueError('Users Must Have A Username')

        # Creating An Object Of The Type (User)
        user                    = self.model(
            user_email          = self.normalize_email(email),
            user_name           = username,
            first_name          = f_name,
            last_name           = l_name
        )

        # Setting The Password For The User Object We Created Above
        user.set_password(password)

        # Saving The User To The Database
        user.save(using = self._db)

        # Returning The (user) Instance To Where The Function Was Called
        return user

    # Defining A Function That Will Be Used To Create Superusers (Admins)
    def create_superuser(self, email, username, f_name, l_name, password):
        # Creating An Instance Of Type (User)
        user                    = self.create_user(
            user_email          = self.normalize_email(email),
            user_name           = username,
            password            = password,
            first_name          = f_name,
            last_name           = l_name
        )

        # Setting The User We Created Above As An Administrator
        user.is_admin           = True
        user.is_staff           = True
        user.is_superuser       = True

        # Saving The User To The Database
        user.save(using = self._db)

        # Returning The (user) Instance To Where The Function Was Called
        return user

# The Custom (User) Class
class User(AbstractBaseUser):
    # The Data (Columns) For Each User
    user_email                  = models.EmailField(max_length = 120, unique = True)
    user_name                   = models.CharField(max_length = 40, unique = True)
    first_name                  = models.CharField(max_length = 60)
    last_name                   = models.CharField(max_length = 60)
    date_joined                 = models.DateTimeField(auto_now_add = True)
    last_login                  = models.DateTimeField(auto_now = True)
    is_admin                    = models.BooleanField(default = False)
    is_active                   = models.BooleanField(default = True)
    is_staff                    = models.BooleanField(default = False)
    is_superuser                = models.BooleanField(default = False)

    # The Data (Column) That Is Going To Be Used As A Login Identifier
    USERNAME_FIELD              = 'user_email'
    REQUIRED_FIELDS             = ['user_name', 'first_name', 'last_name']

    # Defining How The Output Of An Object Of The (User) Class Will Look Like
    def __str__(self):
        return f'{self.user_email} - ({self.user_name})'

    # Defining A Function That Will Check Whether A User Is Admin Or Not
    def has_perm(self, perm, obj = None):
        return self.is_admin

    # Defining A Function That Will Check Whether A User Is Admin In The Current App Or Not
    def has_module_perms(self, app_label):
        return True


I know the way I comment stuff is incorrect, I am going to leave this habit as I don't need to write comments everywhere, instead focus on code clarity, so other than my bad commenting style, is this approach to creating custom users in Django correct?  

P.S: I use PostgreSQL as my Database
Spacing

At first glance, it looks like you're creating a bunch of variables instead of creating an object and passing default parameters. You should reduce all this extra spacing to make it clear what you're doing. Have a look:

user = self.model(
    user_email=self.normalize_email(email),
    user_name=username,
    first_name=f_name,
    last_name=l_name
)


This goes for both of the places where you do this.

According to PEP 8 there should only be one space before and after the = operator. The issue with how you're creating variables is that all that spaces requires the reader to keep they're gaze straight and follow the line to make sure they know what is being assigned to what. Here's an example in your code:

user_email                  = models.EmailField(max_length = 120, unique = True)
user_name                   = models.CharField(max_length = 40, unique = True)
first_name                  = models.CharField(max_length = 60)
last_name                   = models.CharField(max_length = 60)
date_joined                 = models.DateTimeField(auto_now_add = True)
last_login                  = models.DateTimeField(auto_now = True)
is_admin                    = models.BooleanField(default = False)
is_active                   = models.BooleanField(default = True)
is_staff                    = models.BooleanField(default = False)
is_superuser                = models.BooleanField(default = False)


All this spacing really isn't necessary.

Parameter spacing is also an issue in your code. Contrary to the suggestions above, there should be no spaces when passing default parameters. Have a look:

user_email = models.EmailField(max_length=120, unique=True)
def has_perm(self, perm, obj=None):
user.save(using=self._db)


Type Hints

You can use type hints to display what types of parameters are accepted and what types are returned by a function. Have a look:

def create_superuser(
    self,
    email: str,
    username: str,
    f_name: str,
    l_name: str, 
    password: str
) -&gt; User: