In the custom LoginSerializer
:
class LoginSerializer(serializers.Serializer):
username = serializers.CharField(required=False, allow_blank=True)
email = serializers.EmailField(required=False, allow_blank=True)
password = serializers.CharField(style={'input_type': 'password'})
def _validate_email(self, email, password):
user = None
if email and password:
user = authenticate(email=email, password=password)
else:
msg = 'must input email and password'
raise exceptions.ValidationError(msg)
return user
def _validate_username(self, username, password):
user = None
if username and password:
user = authenticate(username=username, password=password)
else:
msg = 'must input username and password'
raise exceptions.ValidationError(msg)
return user
def _validate_username_email(self, username, email, password):
user = None
if email and password:
user = authenticate(email=email, password=password)
elif username and password:
user = authenticate(username=username, password=password)
else:
msg = 'must type in email and pwd or username and pwd'
raise exceptions.ValidationError(msg)
return user
def validate(self, attrs):
username = attrs.get('username')
email = attrs.get('email')
password = attrs.get('password')
user = None
if 'allauth' in settings.INSTALLED_APPS:
from allauth.account import app_settings
# Authentication through email
if app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.EMAIL:
user = self._validate_email(email, password)
# Authentication through username
if app_settings.AUTHENTICATION_METHOD == app_settings.AuthenticationMethod.USERNAME:
user = self._validate_username(username, password)
# Authentication through either username or email
else:
user = self._validate_username_email(username, email, password)
else:
# Authentication without using allauth
if email:
try:
username = User.objects.get(email__iexact=email).get_username()
except User.DoesNotExist:
pass
if username:
user = self._validate_username_email(username, '', password)
# Did we get back an active user?
if user:
if not user.is_active:
msg = 'this user can not login'
raise exceptions.ValidationError(msg)
else:
msg = '不能使用提供的信息登录'
raise exceptions.ValidationError(msg)
# If required, is the email verified?
if 'rest_auth.registration' in settings.INSTALLED_APPS:
from allauth.account import app_settings
if app_settings.EMAIL_VERIFICATION == app_settings.EmailVerificationMethod.MANDATORY:
email_address = user.emailaddress_set.get(email=user.email)
if not email_address.verified:
raise serializers.ValidationError('email invalidate')
return attrs
the login panel is this:
I want to optimize the login panel to two fields. one for username/telphone/email
, the other for password
.
But how to change the LoginSerializer
?
You can easily replace those 3 fields (Email/Username/PhoneNumber), with one CharField and then try querying for all of the 3 fields to find a match, in the easiest case.
Somewhat more elaborate is a way to distinguish between different login types by using simple RegExes or even simple checks, to reduce the query load on your database.
(You might need to use some more complex checks to determine a phone login, depending on the type of your accepted phone numbers, e.g. +1 813 3181, +1 (317) 173-1375, ...)
So all in all, something like the following would do:
def validate(self, attrs):
id_field = attrs.get('id_field')
user = None
if '@' in id_field:
user = User.objects.filter(email__iexact=id_field).first()
elif id_field.isdigit():
user = User.objects.filter(phone_number=id_field).first()
else:
user = User.objects.filter(username__iexact=id_field).first()
if not user:
raise serializers.ValidationError("User does not exists")
...
return attrs
EDIT
To check the password for the found user, you can use Django's check_password function
Password validation would be something like:
...
if not user.check_password(password):
raise ValidationError('Invalid password')
...
# Now on, user's password is validated.