使用自定义用户模型时,扩展AbstractBaseUser
,当USERNAME_FIELD指向可为空的字段时,我遇到了问题
我想要完成的是允许多种登录方法(Facebook,用户名),为此我有两个唯一的标识符字段(facebook_id,用户名),但两者都必须可以为空,因为帐户不需要两者都有(而且大多数帐户都没有)
因此,使用用户名登录时,我依赖于Django内置的登录身份验证后端
它崩溃的时候是尝试使用其他登录方法(facebook)登录并首先尝试使用Django auth
class ModelBackend(object):
"""
Authenticates against settings.AUTH_USER_MODEL.
"""
def authenticate(self, username=None, password=None, **kwargs):
UserModel = get_user_model()
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
user = UserModel._default_manager.get_by_natural_key(username)
if user.check_password(password):
return user
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a non-existing user (#20760).
UserModel().set_password(password)
if username is None
hack用于修复用户名字段实际上不被称为“用户名”的情况,因此username
参数为空,但实际提供的用户名可以在{{1}中找到}}
问题是无论如何它都以无值结束(因为kwargs包含facebook_id = 12345)并最终将其传递给kwargs
get_by_natural_key
大致翻译为def get_by_natural_key(self, username):
return self.get(**{self.model.USERNAME_FIELD: username})
由于我的用户名字段可以为空,这会返回多个结果,整个事件抛出一个models.MultipleObjectsReturned异常(“get()返回多个用户 - 它返回417!”)并阻止任何其他auth后端运行
从我的角度来看,有几个问题:
User.objects.get(username=None)
已知的解决方法:
我知道我可以将ModelBackend移动到最后尝试的方法但是这有时会使得哑查询运行而不是总是运行,我宁愿修复它,所以它永远不会。我也稍微关心它的性能,因为用户名=无用户增长
我也知道我可以扩展ModelBackend,修补漏洞并且不使用库存ModelBackend,但我不知道是否有任何问题,使用ModelBackend会丢失吗?
出于简化的目的,我跳过了实际使用可空的电子邮件字段作为用户名的部分:D我知道为此我可以轻松使用自定义身份验证后端但是一旦我得到了nullable-email-field-login工作我开始认为我可以通过使用户名字段可以为空来为自己来自Facebook的用户生成用户名(并让他们在此过程中选择一个未使用的用户名)时节省一些头痛:D
我担心有一个可以为空的USERNAME_FIELD会让Django以各种不同的方式失败,我没有看到任何警告或解释原因。愚蠢的是,支持多个登录方案的系统无法在其库存auth标识符为空的情况下存活。
答案 0 :(得分:2)
由于您已使用自己的模型替换默认的User
模型,因此我需要修补其经理的get_by_natural_key
方法,以便在DoesNotExist
时提出None
使用None
自然键调用,因为这种情况清楚地表明您的用户正在使用其他方案进行身份验证。您的模型不存在记录可以具有合法DoesNotExist
自然键的情况,因此在任何情况下都应该为此类密钥引发{{1}}。