Login with username or email in Django.
we covered many things in django auth app but what if we want
to login with email instead of username ? or both?.
It's very easy to override the authenticate function to login with email or username.
if you didn't read django auth posts you can have a look at them part 1 , part 2 , part 3 , part 4 .
First we will create new file inside our users app called
backends.py, and we will extend the Django ModelBackend and override authenticate function.
# users/backends.py
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from django.db.models import Q
UserModel = get_user_model()
class EmailBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
try:
user = UserModel.objects.get(
Q(username__iexact=username) | Q(email__iexact=username)
)
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a nonexistent user (#20760).
UserModel().set_password(password)
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
I didn't came up with that code I took it from the django source code at django/contrib/auth/backends.py and just edited the query of checking the user credentials to check with email or username.
Now we need to use this in settings.py.
# settings.py
AUTHENTICATION_BACKENDS = ['users.backends.EmailBackend']
And that's it user can login with either username or email.
If you followed up with our previous posts you will see that we created a custom login form, so we will have to edit it too.
# users/forms.py
...
...
from django.db.models import Q # New
class CustomLoginForm(AuthenticationForm):
username = forms.CharField(
widget=forms.TextInput(attrs={"placeholder": "User Name Or Email"})
)
password = forms.CharField(
label="Password",
widget=forms.PasswordInput(attrs={"placeholder": "Password"}),
)
def clean(self):
username = self.cleaned_data.get("username")
password = self.cleaned_data.get("password")
if username is not None and password:
self.user_cache = authenticate(
self.request, username=username, password=password
)
if self.user_cache is None:
try:
user = User.objects.get(
Q(username__iexact=username) | Q(email__iexact=username) # New
)
except User.DoesNotExist:
user = None
if user is not None:
if not user.is_active:
self.confirm_login_allowed(user)
else:
raise self.get_invalid_login_error()
else:
raise self.get_invalid_login_error()
else:
self.confirm_login_allowed(self.user_cache)
return self.cleaned_data
def confirm_login_allowed(self, user):
if not user.is_active:
error = "The account is closed, please contact us for more details."
raise forms.ValidationError(error)
We can also edit our RegisterForm to make email requierd in registration progress.
# users/forms.py
...
...
class RegisterForm(UserCreationForm):
def clean_email(self):
email = self.cleaned_data["email"]
if email:
match = User.objects.filter(email=email)
if match:
raise forms.ValidationError("This email address is already in use")
return email
def __init__(self, *args, **kwargs): # New
super(RegisterForm, self).__init__(*args, **kwargs)
self.fields["email"].required = True
class Meta:
model = User
fields = ("username", "first_name", "last_name", "email")
We have clean_email function to check if email exists first, you can read Check User's Email Existence for more information.
And we are done!
I hope the tutorial was simple.
Feel free to reach out if you have any questions.
Join to discord Server Here - Django Learn Together.
Views: 313