Django FormView表单无效时get_context_data抛KeyError如何解决
问题根源分析
你遇到的报错和上下文异常问题核心来自3个逻辑错误:
- super调用错误:
get_context_data中super的第一个参数写的是CredentialsView,而非当前类TestView,会导致父类上下文获取逻辑异常 - 核心
KeyError原因:表单校验失败时触发form_invalid逻辑,此时当前请求是POST方法,你直接将POST请求传入仅绑定了get动作的TestViewSet,导致ViewSet没有返回预期的包含用户信息的结构,data字典中不存在username键 - 冗余逻辑:
form_valid方法是表单校验通过后才会触发的钩子,无需在内部再次调用form.is_valid()判断
解决方案
问题1:表单校验无效时正常获取上下文数据
优先直接从当前登录用户对象读取属性,避免跨ViewSet调用带来的请求方法兼容问题,修改后代码如下:
class TestView(LoginRequiredMixin, FormView): form_class = TestForm template_name = 'test.html' def get_context_data(self, **kwargs): # 修正super调用的类名 context = super(TestView, self).get_context_data(**kwargs) # LoginRequiredMixin已经保证用户登录,该判断可按需保留 if self.request.user.is_authenticated: # 直接从当前登录用户对象取属性,无请求方法兼容问题 context['username'] = self.request.user.username context['firstname'] = self.request.user.first_name context['lastname'] = self.request.user.last_name # --- 如果必须调用TestViewSet获取数据,使用以下兼容代码 --- # from rest_framework.request import Request # 构造GET请求副本,适配ViewSet的get动作要求 # get_request = Request(self.request._request) # get_request.method = 'GET' # data = TestViewSet.as_view({'get': 'list'})(get_request).data # 增加安全判断避免KeyError # context['username'] = data.get('username', '') # context['firstname'] = data.get('firstname', '') # context['lastname'] = data.get('lastname', '') return context def form_valid(self, form): password = form.cleaned_data['password'] # 移除冗余的is_valid判断,form_valid触发时已经校验通过 return self.query_api(password)
问题2:表单校验无效时返回错误信息到HTML页面
Django的FormView原生form_invalid方法已经自动将携带错误信息的form对象注入到上下文的form变量中,不需要额外编写视图逻辑,直接在模板中按以下方式渲染即可:
<form method="post"> {% csrf_token %} <!-- 渲染全局非字段错误 --> {% if form.non_field_errors %} <div class="error-box"> {% for error in form.non_field_errors %} <p>* {{ error }}</p> {% endfor %} </div> {% endif %} <!-- 渲染单个表单字段及对应错误 --> <div class="form-item"> <label>密码</label> <input type="password" name="password" value="{{ form.password.value|default:'' }}"> {% if form.password.errors %} <span class="error-tip">{{ form.password.errors.0 }}</span> {% endif %} </div> <button type="submit">提交</button> </form>
- 非字段相关的全局错误存储在
form.non_field_errors中 - 字段级错误存储在对应字段的
errors属性中,字段的已输入值可通过form.字段名.value读取回填
内容的提问来源于stack exchange,提问作者Aditya Singh
相关产品推荐
相关产品推荐

