Django ModelForm提交验证失败 仅渲染双字段时才能通过是什么原因
问题核心原因
- 你在GET请求的处理逻辑中直接修改了
CompanyForm的类属性base_fields,给整个表单类全局新增了一个默认必填的company字段。base_fields是所有表单实例共享的类属性,只要修改一次,后续所有CompanyForm实例(包括POST请求中创建的表单实例)都会自带这个必填的company字段。 - 你的模板仅渲染了
name字段,提交表单时POST数据中没有company字段的对应值,自然无法通过验证。只有同时渲染两个字段并都选中选项,才能满足两个必填字段的验证要求,这就是你遇到的现象的根源。 - 额外风险:直接修改表单类的
base_fields属于线程不安全操作,在多线程部署的生产环境中会出现字段混乱、不同用户拿到其他用户的查询集等严重问题,禁止在实际项目中使用该写法。
解决方法
根据你的实际需求选择对应方案:
方案1:仅需一个公司选择字段(推荐)
你不需要新增额外的company字段,直接动态修改原有name字段的查询集即可,正确做法是将动态逻辑写到表单的__init__方法中:
调整forms.py代码
class CompanyForm(forms.ModelForm): """ 渲染用户关联公司下拉选择组件的表单 """ name = forms.ModelChoiceField(queryset=Company.objects.exclude(name='exclude-all'), required=True) class Meta: model = Company fields = ['name'] def __init__(self, *args, company_queryset=None, **kwargs): super().__init__(*args, **kwargs) # 传入自定义查询集时覆盖默认查询集 if company_queryset is not None: self.fields['name'].queryset = company_queryset
调整views.py代码
GET和POST实例化表单时都传入查询集,同时删除修改base_fields的代码:
def render_entities(request): """渲染实体概览页面的视图""" # 获取当前登录的用户实例 user = User.objects.get(username=request.user.username) user_cashpool = Cashpool.objects.get(company__user=user) # 查询当前资金池关联的所有实体 companies = Company.objects.filter(cashpool=user_cashpool).order_by('name') # 获取当前访问站点的标识 dashboard_site = 'entities' if request.method == 'GET': # 初始化表单并传入自定义查询集 form = CompanyForm(company_queryset=companies) context = { 'company': user.company, 'companies': companies, 'dashboard_site': dashboard_site, 'form': form } return render(request, 'dashboard/dashboard_entities.html', context) else: # POST实例化时同样传入查询集,保证验证规则一致 form = CompanyForm(request.POST, company_queryset=companies) if form.is_valid(): # 获取清洗后的输入值 company = form.cleaned_data['name'] # 后续逻辑不变 ...
方案2:确实需要name和company两个字段
如果你确实需要两个选择字段,同样把字段添加逻辑放到__init__方法中,不要修改base_fields:
调整forms.py代码
class CompanyForm(forms.ModelForm): """ 渲染用户关联公司下拉选择组件的表单 """ name = forms.ModelChoiceField(queryset=Company.objects.exclude(name='exclude-all'), required=True) class Meta: model = Company fields = ['name'] def __init__(self, *args, company_queryset=None, **kwargs): super().__init__(*args, **kwargs) if company_queryset is not None: # 给实例添加company字段,不会影响其他实例 self.fields['company'] = forms.ModelChoiceField(queryset=company_queryset, required=True)
同时模板中需要同时渲染两个字段,或者如果你不需要用户填写company字段,可将其required参数设为False并自行赋值默认值。
内容的提问来源于stack exchange,提问作者JSRB
相关产品推荐
相关产品推荐

