You need to enable JavaScript to run this app. Django项目调用ApplicantEducation查询时抛出DoesNotExist异常的排查
优惠活动
大模型
产品
解决方案
定价
更多

Django项目调用ApplicantEducation查询时抛出DoesNotExist异常的排查

Django 3.1.7 教育信息更新视图异常排查与修复

异常触发根因

根据栈信息定位,抛出DoesNotExist异常的代码行是:
user_info = ApplicantEducation.objects.filter(applicant_info = applicant).get()
具体问题点如下:

  • 这行查询代码放置在视图函数的请求类型分支判断之前,无差别对所有进入视图的请求执行查询逻辑。当当前登录用户从未创建过任何ApplicantEducation教育档案时,ORM的.get()方法因为找不到匹配记录,会直接抛出DoesNotExist异常
  • 原代码写的异常捕获块,位于GET分支下的表单初始化逻辑内部,根本覆盖不到这行前置查询代码的异常抛出位置——用户首次访问页面时,异常触发点还没走到try块就已经栈报错,完全走不到兜底的空表单初始化逻辑
  • 冗余逻辑问题:.filter(查询条件).get()的写法和直接get(查询条件)完全等价,属于无意义的链式调用
  • 业务逻辑bug:POST分支下,当表单校验不通过时,代码会强制重置为无数据的EducationForm()实例,丢失用户已经填写的所有提交内容,导致校验失败后页面回显的表单是空的
  • 能力绕开问题:原代码在GET分支里手动写了一堆initial字段映射来预填表单值,完全绕开了ModelForm通过instance参数自动预填字段的核心能力,维护成本极高

可落地修复方案

核心调整思路:把原来“必须查询到存在的教育记录才能进页面”的强制逻辑,改成“查得到就做更新,查不到就做新建”的兼容逻辑,具体修改点:

  1. 用.first()替代.get()查询教育记录,查不到时返回None而不是抛异常
  2. 把教育记录的查询逻辑和后续表单初始化、提交逻辑打通,有记录就绑定实例做更新,没记录就新建
  3. 修正POST校验失败时的表单重置bug,保留用户提交的内容做回显
  4. 用ModelForm原生的instance参数替代手动写的initial字段映射,减少冗余代码

修复后的完整视图代码:

def UpdateEducation(request):
    context = {}
    user_obj = request.user
    if not user_obj.is_authenticated:
        return redirect('login')

    # 关联查询用户的Applicant、ProfileInfo记录,处理空对象的边界情况
    user_id = Applicant.objects.filter(app_id=user_obj.app_id).first()
    if not user_id:
        return redirect('profile')
    applicant = ProfileInfo.objects.filter(user=user_id).first()
    if not applicant:
        return redirect('profile')

    # 尝试查询当前用户的教育档案,查不到返回None,不抛异常
    user_info = ApplicantEducation.objects.filter(applicant_info=applicant).first()

    if request.POST:
        # 有历史记录就绑定instance做更新,没有就传None走新建流程
        form = EducationForm(request.POST, instance=user_info)
        if form.is_valid():
            obj = form.save(commit=False)
            # 强制绑定当前用户关联的applicant对象,避免前端篡改外键值
            obj.applicant_info = applicant
            obj.save()
            return redirect('profile')
        else:
            # 校验失败时保留用户提交的内容回显,不重置空表单
            context['education_form'] = form
    else:
        # GET请求:有记录就自动通过instance预填所有字段,无记录就返回空表单
        form = EducationForm(instance=user_info)
        context['education_form'] = form

    return render(request, 'admission/signup.html', context)

配套代码优化建议

  • 模型层修正:FloatField不支持max_length参数,原代码中marks_percentage = models.FloatField(max_length=5, default=0.0)的写法在执行迁移时会触发Django警告,应该移除max_length=5参数
  • 表单层修正:原EducationForm里的clean方法写了if self.is_valid()的判断是无效逻辑——Django触发clean流程时才会执行is_valid()方法,进clean方法的时候校验流程还没启动,里面调用self.is_valid()会造成递归校验的问题,应该去掉这个无效的if分支,直接读取self.cleaned_data做自定义校验即可
  • 代码健壮性:原代码用裸try...except捕获所有异常,会隐藏包括语法错误、数据库连接错误在内的所有异常信息,不利于生产环境排查问题,用.first()替代.get()的方案可以从根源上避免DoesNotExist类异常,不需要依赖宽泛的异常捕获做兜底

内容的提问来源于stack exchange,提问作者Asif Iqbal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.30 14:45:07