又一个解决方案:
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
class EmailOrUsernameModelBackend(ModelBackend):
"""
Authentication backend which allows users to authenticate using either their
username or email address
Source: https://stackoverflow.com/a/35836674/59984
"""
def authenticate(self, request, username=None, password=None, **kwargs):
# n.b. Django <2.1 does not pass the `request`
user_model = get_user_model()
if username is None:
username = kwargs.get(user_model.USERNAME_FIELD)
# The `username` field is allows to contain `@` characters so
# technically a given email address could be present in either field,
# possibly even for different users, so we'll query for all matching
# records and test each one.
users = user_model._default_manager.filter(
Q(**{user_model.USERNAME_FIELD: username}) | Q(email__iexact=username)
)
# Test whether any matched user has the provided password:
for user in users:
if user.check_password(password):
return user
if not users:
# Run the default password hasher once to reduce the timing
# difference between an existing and a non-existing user (see
# https://code.djangoproject.com/ticket/20760)
user_model().set_password(password)
修正:
>默认情况下,用户名字段中不禁止@,因此除非自定义用户模型禁止@符号,否则不能用于区分用户名和电子邮件.
>从技术上讲,可能有两个用户使用相同的电子邮件,一个在电子邮件字段中,另一个在用户名中.除非这种可能性受到限制,否则如果使用UserModel._default_manager.get(Q(username__iexact = username)| Q(email__iexact = username)),则可能导致用户无法进行身份验证或未处理MultipleObjectsReturned异常.
>除了以外的任何例外:通常是不好的做法
缺点 – 如果有两个用户,使用相同的电子邮件,一个在用户名中,另一个在电子邮件中,并且他们有相同的密码,那么它很容易验证第一个匹配.我猜这种可能性很小.
另请注意:任何方法都应在User模型中强制使用唯一的电子邮件字段,因为默认的User模型没有定义唯一的电子邮件,如果User.objects.get(email__iexact =“…”),这将导致未处理的异常. )使用,或验证第一场比赛.在任何情况下,使用电子邮件登录都假定电子邮件是唯一的.