18👍
The USERNAME_FIELD
setting does not support a list. You could create a custom authentication backend that tries to look up the user on the ’email’ or ‘username’ fields.
from django.db.models import Q
from django.contrib.auth import get_user_model
MyUser = get_user_model()
class UsernameOrEmailBackend(object):
def authenticate(self, username=None, password=None, **kwargs):
try:
# Try to fetch the user by searching the username or email field
user = MyUser.objects.get(Q(username=username)|Q(email=username))
if user.check_password(password):
return user
except MyUser.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a non-existing user (#20760).
MyUser().set_password(password)
Then, in your settings.py
set AUTHENTICATION_BACKENDS
to your authentication backend:
AUTHENTICATION_BACKENDS = ('path.to.UsernameOrEmailBackend,)\
Note that this solution isn’t perfect. For example, password resets would only work with the field specified in your USERNAME_FIELD
setting.
11👍
We can do that by implementing our own Email authentication backend.
You can do something like below:
Step-1 Substite the custom User model in settings:
Since we would not be using Django’s default User
model for authentication, we need to define our custom MyUser
model in settings.py
. Specify MyUser
as the AUTH_USER_MODEL
in the project’s settings.
AUTH_USER_MODEL = 'myapp.MyUser'
Step-2 Write the logic for the custom authentication backend:
To write our own authentication backend, we need to implement atleast two methods i.e. get_user(user_id)
and authenticate(**credentials)
.
from django.contrib.auth import get_user_model
from django.contrib.auth.models import check_password
class MyEmailBackend(object):
"""
Custom Email Backend to perform authentication via email
"""
def authenticate(self, username=None, password=None):
my_user_model = get_user_model()
try:
user = my_user_model.objects.get(email=username)
if user.check_password(password):
return user # return user on valid credentials
except my_user_model.DoesNotExist:
return None # return None if custom user model does not exist
except:
return None # return None in case of other exceptions
def get_user(self, user_id):
my_user_model = get_user_model()
try:
return my_user_model.objects.get(pk=user_id)
except my_user_model.DoesNotExist:
return None
Step-3 Specify the custom authentication backend in settings:
After writing the custom authentication backend, specify this authentication backend in the AUTHENTICATION_BACKENDS
setting.
AUTHENTICATION_BACKENDS
contains the list of authentication backends to be used. Django tries authenticating across all of its authentication backends. If the first authentication method fails, Django tries the second one, and so on, until all backends have been attempted.
AUTHENTICATION_BACKENDS = (
'my_app.backends.MyEmailBackend', # our custom authentication backend
'django.contrib.auth.backends.ModelBackend' # fallback to default authentication backend if first fails
)
If authentication via MyEmailBackend
fails i.e user could not be authenticated via email
, then we use the Django’s default authentication ModelBackend
which will try to authenticate via username
field of MyUser
model.
- How to test a Django on_commit hook without clearing the database?
- Django + celery – How do I set up a crontab schedule for celery in my django app?
2👍
No, you cannot have more than one field defined in USERNAME_FIELD
.
One option would be to write your own custom login to check for both fields yourself. https://docs.djangoproject.com/en/1.8/topics/auth/customizing/
i.e. change the backend to your own. AUTHENTICATION_BACKENDS
then write an authenticate method and check the username on both fields in the DB.
PS you may want to use unique_together
on your model so you don’t run into problems.
Another option would be to use the actual field username
to store both string and email.
0👍
Unfortunately, not out-of-the box.
The auth contrib module asserts that the USERNAME_FIELD value is mono-valued.
See https://github.com/django/django/search?q=USERNAME_FIELD
If you want to have a multi-valued USERNAME_FIELD, you will either have to write the corresponding logic or to find a package that allow it.