Django中访问不存在Week实例时Http404异常未触发的问题排查
你的判断完全正确,问题出在异常捕获的范围和类型上
为什么当前代码没触发404?
当用户访问不存在的week_id时,week = Week.objects.get(pk=week_id)这一行会直接抛出Week.DoesNotExist异常,但这行代码不在你的try代码块内,所以后面的except Week.DoesNotExist根本没机会捕获这个异常,导致原始的DoesNotExist错误直接暴露出来,而不是触发你定义的404页面。
你的try-except块只覆盖了获取selected_choice的逻辑,完全没处理获取week时的异常,这就是问题所在。
解决方法
这里有两种常用的修复方案,你可以根据习惯选择:
方案一:调整try块范围,覆盖week的获取逻辑
把获取week的代码也放进try块里,同时补充处理其他可能的异常(比如用户没选择选项就提交的情况):
from django.http import Http404, HttpResponseRedirect from django.shortcuts import render, redirect from .models import Week, Choice def vote(request, week_id): try: # 把week的获取也放进try块 week = Week.objects.get(pk=week_id) selected_choice = week.choice_set.get(pk=request.POST["choice"]) except Week.DoesNotExist: # 现在能捕获week不存在的情况了 raise Http404("Poll for said week does not exist") except (KeyError, Choice.DoesNotExist): # 处理用户没选选项或选项不存在的情况,返回投票页面并提示错误 return render(request, 'polls/detail.html', { 'week': week, 'error_message': "You didn't select a valid choice.", }) else: selected_choice.votes += 1 selected_choice.save() return redirect("results", week_id)
方案二:使用Django内置的get_object_or_404快捷函数(更推荐)
Django提供了get_object_or_404工具函数,它会在查询不到对象时自动抛出Http404异常,省去手动写try-except的麻烦,代码更简洁:
from django.shortcuts import get_object_or_404, render, redirect from .models import Week, Choice def vote(request, week_id): # 直接用get_object_or_404,不存在就自动返回404 week = get_object_or_404(Week, pk=week_id) try: selected_choice = week.choice_set.get(pk=request.POST["choice"]) except (KeyError, Choice.DoesNotExist): return render(request, 'polls/detail.html', { 'week': week, 'error_message': "You didn't select a valid choice.", }) else: selected_choice.votes += 1 selected_choice.save() return redirect("results", week_id)
额外提示
原来的代码还有一个潜在问题:当用户没有选择任何选项就提交表单时,request.POST["choice"]会抛出KeyError异常,之前的代码没有处理这种情况,建议像上面的示例一样补充捕获这个异常,提升用户体验。
内容的提问来源于stack exchange,提问作者user7496931
相关产品推荐
相关产品推荐

