So i tried using djoser recently and i want to use email instead of username to login.
Djoser : http://djoser.readthedocs.io/en/latest/index.html
Then i try to customise the token create/login to change from username to email in the serializers.py
Original
class TokenCreateSerializer(serializers.Serializer):
password = serializers.CharField(
required=False, style={'input_type': 'password'}
)
default_error_messages = {
'invalid_credentials': constants.INVALID_CREDENTIALS_ERROR,
'inactive_account': constants.INACTIVE_ACCOUNT_ERROR,
}
def __init__(self, *args, **kwargs):
super(TokenCreateSerializer, self).__init__(*args, **kwargs)
self.user = None
self.fields[User.USERNAME_FIELD] = serializers.CharField(
required=False
)
def validate(self, attrs):
self.user = authenticate(
username=attrs.get(User.USERNAME_FIELD),
password=attrs.get('password')
)
self._validate_user_exists(self.user)
self._validate_user_is_active(self.user)
return attrs
def _validate_user_exists(self, user):
if not user:
self.fail('invalid_credentials')
def _validate_user_is_active(self, user):
if not user.is_active:
self.fail('inactive_account')
Edited
class TokenCreateSerializer(serializers.Serializer):
password = serializers.CharField(
required=False, style={'input_type': 'password'}
)
default_error_messages = {
'invalid_credentials': constants.INVALID_CREDENTIALS_ERROR,
'inactive_account': constants.INACTIVE_ACCOUNT_ERROR,
}
def __init__(self, *args, **kwargs):
super(TokenCreateSerializer, self).__init__(*args, **kwargs)
self.user = None
self.fields[User.EMAIL_FIELD] = serializers.EmailField(
required=False
)
def validate(self, attrs):
self.user = authenticate(
email=attrs.get(User.EMAIL_FIELD),
password=attrs.get('password')
)
self._validate_user_exists(self.user)
self._validate_user_is_active(self.user)
return attrs
def _validate_user_exists(self, user):
if not user:
self.fail('invalid_credentials')
def _validate_user_is_active(self, user):
if not user.is_active:
self.fail('inactive_account')
but the result i get in the api is this
{
"non_field_errors": [
"Unable to login with provided credentials."
]
I did try other method but all have same result. Is there a way to make it using of email to authenticate instead of username ?
Djoser
uses authenticate
method from django.contrib.auth
. By default the AUTHENTICATION_BACKENDS
is set to usedjango.contrib.auth.backends.ModelBackend
which will try to get the user model using default manager get_by_natural_key
method, which does:
def get_by_natural_key(self, username):
return self.get(**{self.model.USERNAME_FIELD: username})
You can check the source code for this.
My approach on this was to enhance django's UserManager
, assuming your User
model is subclass of AbstractUser
, by trying to get the user by email or username, like this:
from django.contrib.auth.models import UserManager
from django.db.models import Q
class CustomUserManager(UserManager):
def get_by_natural_key(self, username):
return self.get(
Q(**{self.model.USERNAME_FIELD: username}) |
Q(**{self.model.EMAIL_FIELD: username})
)
Use that in your User
model
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
...
objects = CustomUserManager()
You should now be able to login using email or username