July, 2023 Update:
You can set up authentication with email and password instead of username and password and in this instruction, username is removed and I tried not to change the default Django settings as much as possible. *You can also see my answer explaining how to extend User model with OneToOneField() to add extra fields and you can see my answer and my answer explaining the difference between AbstractUser and AbstractBaseUser.
First, run the command below to create account app:
python manage.py startapp account
Then, set account app to INSTALLED_APPS and set AUTH_USER_MODEL = 'account.User' in settings.py as shown below:
# "settings.py"
INSTALLED_APPS = [
...
"account", # Here
]
AUTH_USER_MODEL = 'account.User' # Here
Then, create managers.py just under account folder and create UserManager class extending (UM)UserManager in managers.py as shown below. *Just copy & paste the code below to managers.py and managers.py is necessary to make the command python manage.py createsuperuser work properly without any error:
# "account/managers.py"
from django.contrib.auth.models import UserManager as UM
from django.contrib.auth.hashers import make_password
class UserManager(UM):
def _create_user(self, email, password, **extra_fields):
if not email:
raise ValueError("The given email must be set")
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.password = make_password(password)
user.save(using=self._db)
return user
def create_user(self, email=None, password=None, **extra_fields):
extra_fields.setdefault("is_staff", False)
extra_fields.setdefault("is_superuser", False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email=None, password=None, **extra_fields):
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
if extra_fields.get("is_staff") is not True:
raise ValueError("Superuser must have is_staff=True.")
if extra_fields.get("is_superuser") is not True:
raise ValueError("Superuser must have is_superuser=True.")
return self._create_user(email, password, **extra_fields)
Then, create User model extending AbstractUser and remove username by setting it None and set email with unique=True and set email to USERNAME_FIELD and set UserManager to objects in account/models.py as shown below. *Just copy & paste the code below to account/models.py:
# "account/models.py"
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import AbstractUser
from .managers import UserManager
class User(AbstractUser):
username = None # Here
email = models.EmailField(_("email address"), unique=True) # Here
USERNAME_FIELD = 'email' # Here
REQUIRED_FIELDS = []
objects = UserManager() # Here
Or, you can also create User model extending AbstractBaseUser and PermissionsMixin as shown below. *This code below with AbstractBaseUser and PermissionsMixin is equivalent to the code above with AbstractUser:
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.utils import timezone
from .managers import UserManager
class User(AbstractBaseUser, PermissionsMixin):
first_name = models.CharField(_("first name"), max_length=150, blank=True)
last_name = models.CharField(_("last name"), max_length=150, blank=True)
email = models.EmailField(_("email address"), unique=True)
is_staff = models.BooleanField(
_("staff status"),
default=False,
help_text=_("Designates whether the user can log into this admin site."),
)
is_active = models.BooleanField(
_("active"),
default=True,
help_text=_(
"Designates whether this user should be treated as active. "
"Unselect this instead of deleting accounts."
),
)
date_joined = models.DateTimeField(_("date joined"), default=timezone.now)
USERNAME_FIELD = 'email'
objects = UserManager() # Here
class Meta:
verbose_name = _("user")
verbose_name_plural = _("users")
*Don't extend DefaultUser(User) model as shown below otherwise there is error:
# "account/models.py"
from django.contrib.auth.models import User as DefaultUser
class User(DefaultUser):
...
Then, create UserAdmin class extending UA(UserAdmin) in account/admin.py as shown below. *Just copy & paste the code below to account/admin.py:
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.admin import UserAdmin as UA
from .models import User
@admin.register(User)
class UserAdmin(UA):
fieldsets = (
(None, {"fields": ("password",)}),
(_("Personal info"), {"fields": ("first_name", "last_name", "email")}),
(
_("Permissions"),
{
"fields": (
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
),
},
),
(_("Important dates"), {"fields": ("last_login", "date_joined")}),
)
add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": ("email", "password1", "password2"),
},
),
)
list_display = ("email", "first_name", "last_name", "is_staff")
ordering = ("-is_staff",)
readonly_fields=('last_login', 'date_joined')
Then, run the command below. *This must be the 1st migration to database when customizing User model in this way otherwise there is error according to my experiments and the doc so before you develop your Django project, you must first create custom User model:
python manage.py makemigrations && python manage.py migrate
Then, run the command below:
python manage.py createsuperuser
Then, run the command below:
python manage.py runserver 0.0.0.0:8000
Then, open the url below:
http://localhost:8000/admin/login/
Finally, you can log in with email and password as shown below:

And, this is Add custom user page as shown below:
