直接继承AbstractUser不能解决自定义登录校验,因其仅定义字段结构,不参与认证逻辑;Django登录由authenticate()调用AuthenticationBackend完成,与模型继承无关,必须自定义后台类并注册到AUTHENTICATION_BACKENDS首位。
为什么直接继承
不能解决自定义登录校验?
因为
只定义了用户字段结构,不参与认证逻辑;Django 的登录流程由
调用认证后台(
)完成,和模型继承无关。哪怕你重写了
或加了自定义字段,只要后台没改,
仍按默认规则(用户名+密码)查
模型,根本不会走到你的逻辑里。
常见错误现象:
返回
,但数据库里明明有该用户;或者登录成功了,却没触发你写的邮箱验证、状态检查等逻辑。
必须实现一个继承
的自定义后台类
在
中显式注册它,覆盖默认后台
重写
方法,在其中调用你自己的校验逻辑(比如支持邮箱/手机号登录、检查
细粒度状态、临时冻结等)
如何写一个支持邮箱或手机号登录的认证后台?
核心是重写
:解析传入的
参数,判断它是邮箱、手机号还是用户名,再用对应字段查库。注意别硬编码字段名,要用
或
配合
查询。
立即学习
“
Python免费学习笔记(深入)
”;
关键点:
go语言参考手册 中文CHM版
Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译
下载
是
自带方法,会检查
和
(如果启用了
),建议保留
不要在
里抛异常,返回
即可,Django 登录视图会安静处理
如果用了自定义字段(如
),确保该字段在模型中已定义且允许为空或有唯一约束,否则查库可能出错
settings.py 中怎么正确配置认证后台?
必须把自定义后台放在
列表首位,Django 按顺序调用每个后台的
,只要有一个返回用户对象就终止。如果放后面,可能被默认后台提前“截胡”——比如用户名匹配成功,但你本意是让邮箱优先。
配置示例(假设后台文件在
):
容易踩的坑:
路径写错(比如漏了
或模块名大小写不符),导致启动时报
忘记保留
,导致管理后台(
)无法登录
多个后台共存时,不同后台对同一
的解释冲突(比如一个按邮箱查,一个按用户名查),引发不可预期行为
继承
后还要注意什么?
主要是字段兼容性和迁移问题。Django 默认的
模型字段(如
、
)在
里已定义,如果你要改字段行为(比如让
成为主键、取消
必填),不能只改模型声明,还得同步调整认证后台里的查询逻辑和表单验证。
若设
为唯一且非空,记得在后台里把
字段查询删掉,否则查不到用户
新增字段(如
)要在
里显式检查,Django 不会自动拦截
运行
前确认已有数据能兼容新字段约束,否则迁移会卡住
最常被忽略的是:后台类里查库用的字段,必须和模型实际字段一致;而模型字段是否生效,又取决于迁移是否成功执行——这两步脱节,调试时容易绕晕。
AbstractUserAbstractUserauthenticate()AuthenticationBackendcheck_passwordauthenticate()Userauthenticate(username='xxx', password='yyy')NoneModelBackendsettings.pyauthenticate()is_activeauthenticate()usernameget_by_natural_key()filter()Qfrom django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from django.db.models import Q
class EmailOrPhoneBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
UserModel = get_user_model()
try:
先尝试邮箱
user = UserModel.objects.get(Q(email=username) | Q(phone=username) | Q(username=username))
except UserModel.DoesNotExist:
return None
if user.check_password(password) and self.user_can_authenticate(user):
return user
return Noneself.user_can_authenticate(user)ModelBackendis_activeis_staffrequire_staff_statusauthenticate()NonephoneAUTHENTICATION_BACKENDSauthenticate()myapp/auth_backends.pyAUTHENTICATION_BACKENDS = [
'myapp.auth_backends.EmailOrPhoneBackend',
'django.contrib.auth.backends.ModelBackend', # 保留默认,作为兜底
]
myapp.ImproperlyConfiguredModelBackend/admin/usernameAbstractUserUserusernameemailAbstractUseremailusernameemailusernameis_email_verifiedauthenticate()python manage.py makemigrations