Django表单绑定异常:提交后form.is_bound返回False无法验证
表单绑定后
is_bound返回False且验证失败的原因及解决 问题背景
开发一个选择Sale ID并提交至服务器删除的表单,执行form = DeleteSalesForm(request.POST)绑定后,form.is_bound返回False,表单无法通过验证。相关代码及报错日志如下:
views.py代码
def delete_sales(request): if not request.user.is_authenticated: return redirect('/login') if request.method == 'GET': form = DeleteSalesForm() print("GET") return render(request, 'inventorymanagment/delete_sales.html', {'form': form}) form = DeleteSalesForm(request.POST) print("checking if form is valid", request.POST, form.is_bound, form) if form.is_valid(): form.delete_sale() return redirect('/') else: print("sale not deleted") print(form.errors) return render(request, 'inventorymanagment/delete_sales.html', {'form': form})
forms.py代码
class DeleteSalesForm(ModelForm): class Meta: model = Sales fields = ['id'] labels = { 'id': 'Sale', } def clean(self): self.cleaned_data = super().clean() id = int(self.cleaned_data.get('id')[0]) print('id', id) if id <= 0: raise forms.ValidationError('ID cannot be negative') print('id > 0', id) if id not in Sales.objects.all().values_list('id', flat=True): raise forms.ValidationError('ID not in database') print("id in database", id) return id def __init__(self, *args, **kwargs): super().__init__() self.fields['id'] = forms.ChoiceField( label="Sale", choices=[(i.id, i) for i in Sales.objects.all()], required=True) def delete_sale(self): id = self.cleaned_data.get('id') sale = Sales.objects.get(id=id) item = sale.item item.remaining += sale.quantity item.sold -= sale.quantity item.save() sale.delete() return True def validate_unique(self) -> None: print("validate_unique") return super().validate_unique() def validate_constraints(self) -> None: print("validate_constraints") return super().validate_constraints()
报错日志
[31/Aug/2022 15:38:36] "POST /delete_sales HTTP/1.1" 200 932 checking if form is valid <QueryDict: {'csrfmiddlewaretoken': ['q3aga7bySuscQ6bb9zrh3pFUlfoIoRoGQnHvQQ8LlbiEfUl1XDSGp2fk3nCj5KJk'], 'id': ['16']}> False <tr> <th><label for="id_id">Sale:</label></th> <td> <select name="id" id="id_id"> <option value="16">id: 16, iphone 6 quantity: 50 price: 30.00 day: 2022-08-31</option> <option value="17">id: 17, Iphone 8 quantity: 70 price: 80.00 day: 2022-08-31</option> </select> </td> </tr> sale not deleted
问题原因
- 表单未正确绑定数据:
__init__方法调用父类构造器时未传递*args, **kwargs,导致父类无法识别传入的request.POST数据,is_bound始终为False。 clean方法返回值错误:clean方法要求返回完整的cleaned_data字典,但当前代码返回单个id值,破坏了表单的验证流程。- ID取值错误:从ChoiceField获取的
id是字符串类型(如'16'),代码中错误使用[0]索引取值,会引发索引错误。
解决方案
修正后的forms.py代码
class DeleteSalesForm(ModelForm): class Meta: model = Sales fields = ['id'] labels = { 'id': 'Sale', } def clean(self): cleaned_data = super().clean() sale_id = cleaned_data.get('id') # 处理ID转换,添加异常捕获 try: sale_id = int(sale_id) except (ValueError, TypeError): raise forms.ValidationError('无效的Sale ID') if sale_id <= 0: raise forms.ValidationError('ID不能为负数') # 用exists()判断ID是否存在,性能更优 if not Sales.objects.filter(id=sale_id).exists(): raise forms.ValidationError('该ID不存在于数据库中') # 更新cleaned_data并返回完整字典 cleaned_data['id'] = sale_id return cleaned_data def __init__(self, *args, **kwargs): # 传递所有参数给父类构造器 super().__init__(*args, **kwargs) self.fields['id'] = forms.ChoiceField( label="Sale", choices=[(i.id, i) for i in Sales.objects.all()], required=True) def delete_sale(self): sale_id = self.cleaned_data.get('id') sale = Sales.objects.get(id=sale_id) item = sale.item item.remaining += sale.quantity item.sold -= sale.quantity item.save() sale.delete() return True
关键修正说明
- 修复表单绑定:
super().__init__(*args, **kwargs)确保父类能接收POST数据,is_bound会正确变为True。 - 修正
clean方法:返回完整的cleaned_data字典,保证验证流程正常执行;移除错误的索引操作,添加异常处理避免转换失败。 - 优化存在性检查:使用
filter().exists()替代values_list遍历,减少数据库查询开销。
内容的提问来源于stack exchange,提问作者Hack3r
相关产品推荐
相关产品推荐

