Django密码重置报错:'NoneType'无'user'属性问题排查与解决
问题描述
我需要通过邮件实现密码重置功能,目前ForgetPassword视图运行正常,但ChangePassword视图中的profile_obj返回None,导致出现错误'NoneType' object has no attribute 'user',尝试多种方法均未解决,恳请排查问题根源并提供可行解决方案。
相关代码
views.py
def ForgetPassword(request): try: if request.method == 'POST': email = request.POST.get('email') if not User.objects.filter(email=email).first(): messages.warning(request, 'Not email found with this email.') return redirect('ForgetPassword') user_obj = User.objects.get(email = email) token = str(uuid.uuid4()) send_forget_password_mail(user_obj.email , token) messages.success(request, 'Please check your mail box an email is send.') return redirect('ForgetPassword') except Exception as e: print(e) context = { } return render(request, "forget_password_email.html", context) def ChangePassword(request, token): context = {} try: profile_obj = User.objects.filter(forget_password_token=token).first() print(profile_obj) if request.method == 'POST': new_password = request.POST.get('new_password') confirm_password = request.POST.get('reconfirm_password') user_id = request.POST.get('user_id') if user_id is None: messages.warning(request, 'No user id found.') return redirect(f'/ChangePassword/{token}/') if new_password != confirm_password: messages.warning(request, 'both should be equal.') return redirect(f'/ChangePassword/{token}/') profile_obj.password = new_password profile_obj.save() user_obj = User.objects.get(id = user_id) user_obj.set_password(new_password) user_obj.save() return redirect('Login') context = {'user_id' : profile_obj.user.id} except Exception as e: print(e) context = { } return render(request,'change_password.html', context)
helpers.py
from django.core.mail import send_mail from django.conf import settings def send_forget_password_mail(email , token ): subject = 'Your forget password link' message = f'Hi , click on the link to reset your password http://127.0.0.1:8000/ChangePassword/{token}/' email_from = settings.EMAIL_HOST_USER recipient_list = [email] send_mail(subject, message, email_from, recipient_list) return True
models.py
class User(AbstractUser): email = models.EmailField(max_length=50, unique=True, error_messages={"unique":"The email must be unique!"}) REQUIRES_FIELDS = ["email"] objects = CustomeUserManager() forget_password_token = models.CharField(null= True, max_length=100, blank=True) def __str__(self): return f"{self.pk}.{self.email}"
urls.py
path('ForgetPassword/', views.ForgetPassword, name="ForgetPassword"), path('ChangePassword/<token>/', views.ChangePassword, name="ChangePassword")
问题根源排查
核心问题:生成的token未保存到用户模型
ForgetPassword视图中生成了重置token,但仅传给邮件发送函数,并未将token赋值给用户的forget_password_token字段并保存到数据库,导致ChangePassword视图用token查询时无法找到对应用户,返回None。错误的属性访问
profile_obj本身就是User实例(继承自AbstractUser),不存在user属性,代码中profile_obj.user.id属于错误访问,应该直接使用profile_obj.id。重复清空context
ChangePassword视图末尾重新将context赋值为空字典,导致即使前面正确获取了user_id,模板也无法接收到该参数。密码设置方式错误
直接给profile_obj.password赋值明文密码,不会进行哈希处理,会导致用户后续无法正常登录,应该使用set_password()方法。
修复方案
1. 修复ForgetPassword视图,保存token到用户模型
def ForgetPassword(request): try: if request.method == 'POST': email = request.POST.get('email') user_obj = User.objects.filter(email=email).first() if not user_obj: messages.warning(request, '未找到该邮箱对应的用户。') return redirect('ForgetPassword') # 生成并保存token到用户模型 token = str(uuid.uuid4()) user_obj.forget_password_token = token user_obj.save() send_forget_password_mail(user_obj.email, token) messages.success(request, '密码重置邮件已发送,请查收邮箱。') return redirect('ForgetPassword') except Exception as e: print(e) return render(request, "forget_password_email.html", {})
2. 修复ChangePassword视图的多个问题
def ChangePassword(request, token): context = {} try: profile_obj = User.objects.filter(forget_password_token=token).first() # 处理无效token的情况 if not profile_obj: messages.warning(request, '无效或已过期的重置链接。') return redirect('ForgetPassword') if request.method == 'POST': new_password = request.POST.get('new_password') confirm_password = request.POST.get('reconfirm_password') if not new_password or not confirm_password: messages.warning(request, '请填写完整密码信息。') return redirect(f'/ChangePassword/{token}/') if new_password != confirm_password: messages.warning(request, '两次输入的密码不一致。') return redirect(f'/ChangePassword/{token}/') # 正确哈希设置密码,同时重置token避免重复使用 profile_obj.set_password(new_password) profile_obj.forget_password_token = "" profile_obj.save() messages.success(request, '密码重置成功,请登录。') return redirect('Login') # 正确传递user_id context = {'user_id': profile_obj.id} except Exception as e: print(e) messages.error(request, '密码重置过程中出现错误,请重试。') # 不再清空context return render(request,'change_password.html', context)
3.(可选)优化邮件链接,避免硬编码
在settings.py中添加站点域名配置:
SITE_DOMAIN = "http://127.0.0.1:8000"
修改helpers.py用反向解析生成链接:
from django.core.mail import send_mail from django.conf import settings from django.urls import reverse def send_forget_password_mail(email, token): subject = '密码重置链接' reset_url = f"{settings.SITE_DOMAIN}{reverse('ChangePassword', kwargs={'token': token})}" message = f'您好,请点击以下链接重置您的密码:\n{reset_url}' email_from = settings.EMAIL_HOST_USER recipient_list = [email] send_mail(subject, message, email_from, recipient_list) return True
内容的提问来源于stack exchange,提问作者Mossaddak
相关产品推荐
相关产品推荐

