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: