You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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")

问题根源排查
  1. 核心问题:生成的token未保存到用户模型
    ForgetPassword视图中生成了重置token,但仅传给邮件发送函数,并未将token赋值给用户的forget_password_token字段并保存到数据库,导致ChangePassword视图用token查询时无法找到对应用户,返回None。

  2. 错误的属性访问
    profile_obj本身就是User实例(继承自AbstractUser),不存在user属性,代码中profile_obj.user.id属于错误访问,应该直接使用profile_obj.id。

  3. 重复清空context
    ChangePassword视图末尾重新将context赋值为空字典,导致即使前面正确获取了user_id,模板也无法接收到该参数。

  4. 密码设置方式错误
    直接给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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 03:55:26