如何让Django为无可用密码的社交登录用户发送重置密码邮件?
解决Django无密码用户无法触发重置密码邮件的问题
我之前也碰到过一模一样的场景——社交登录创建的用户根本没设置过密码,想切换成邮箱密码登录时,发密码重置邮件居然没反应!查了才知道Django默认会跳过那些没有可用密码的用户,默认认为这类用户应该用LDAP之类的认证方式,但咱们的需求就是要让这些用户通过重置邮件来设置密码,总不能注册时给个随机密码,用户根本搞不清状况对吧?
别急,咱们只需要两步就能搞定:
1. 自定义密码重置表单,跳过可用密码检查
Django自带的PasswordResetForm里,get_users方法会过滤掉has_usable_password()返回False的用户。咱们要做的就是重写这个方法,把这个过滤条件去掉。
在你的app里新建(或修改)forms.py,加入以下代码:
from django.contrib.auth.forms import PasswordResetForm from django.contrib.auth import get_user_model UserModel = get_user_model() class CustomPasswordResetForm(PasswordResetForm): def get_users(self, email): """重写方法,包含无可用密码的活跃用户""" email_field_name = UserModel.get_email_field_name() # 只筛选活跃且邮箱匹配的用户,去掉密码可用性检查 active_users = UserModel._default_manager.filter(**{ '%s__iexact' % email_field_name: email, 'is_active': True, }) return (u for u in active_users)
2. 在URL配置中替换默认的重置视图
接下来,咱们要告诉Django使用咱们自定义的表单来处理密码重置请求。修改项目的urls.py:
from django.contrib.auth import views as auth_views from .forms import CustomPasswordResetForm # 注意替换成你的forms实际路径 urlpatterns = [ # 替换默认的密码重置视图,指定用自定义表单 path('password-reset/', auth_views.PasswordResetView.as_view( form_class=CustomPasswordResetForm ), name='password_reset'), # 下面这些默认视图不用改,直接保留就行 path('password-reset/done/', auth_views.PasswordResetDoneView.as_view(), name='password_reset_done'), path('reset/<uidb64>/<token>/', auth_views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'), path('reset/done/', auth_views.PasswordResetCompleteView.as_view(), name='password_reset_complete'), ]
为什么这样可行?
修改后,不管用户有没有设置过密码,只要是活跃用户且邮箱匹配,就能收到重置邮件。而当用户点击邮件里的链接后,Django自带的PasswordResetConfirmView会用SetPasswordForm让用户设置新密码——这个表单本来就不需要旧密码,刚好适配无密码用户的场景,设置完成后用户的密码就会被正常哈希存储,之后就能用邮箱密码登录啦。
这样就完美避开了注册时设置随机密码的尴尬,流程对用户来说也更直观~
内容的提问来源于stack exchange,提问作者jerrymouse




