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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With