You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django动态表单集为空时仍可提交 如何实现非空校验阻止提交

问题根源

  1. 视图逻辑错误:原代码只要患者主表单校验通过就会直接保存患者数据并跳转,完全不阻塞诊断表单集校验失败的场景,就算诊断信息不符合要求也会提交成功,用户自然看不到校验提示。
  2. GET请求中主表单未实例化:原GET方法里直接把PatientForm类传给模板,没有实例化,会导致前端渲染的表单缺少浏览器端自带的必填校验属性。
  3. 缺少表单集自定义校验:仅靠默认的min_num校验不足以覆盖用户新增行后全部留空、或者删除所有有效行的场景。

修复方案

1. 修正forms.py,增加表单集全局校验

新增自定义表单集基类,强制校验至少存在一条有效诊断数据:

from django.forms import BaseInlineFormSet, forms

class PatientForm(ModelForm):
    class Meta:
        model = Patient
        fields = ['age']

class BaseDiagnosisFormSet(BaseInlineFormSet):
    def clean(self):
        super().clean()
        valid_count = 0
        for form in self.forms:
            # 跳过标记为删除的行
            if self.can_delete and self._should_delete_form(form):
                continue
            # 统计填写了诊断信息的有效行
            if form.cleaned_data and form.cleaned_data.get('diag_name'):
                valid_count += 1
        if valid_count < 1:
            raise forms.ValidationError("至少需要填写一条有效的诊断信息")

DiagnosisFormSet = inlineformset_factory(
    Patient, Diagnosis, 
    formset=BaseDiagnosisFormSet,
    fields=("diag_name", ), 
    extra=0, 
    min_num=1, 
    validate_min=True,
    can_delete=True
)

2. 修正views.py,调整校验和保存逻辑

必须主表单和表单集同时校验通过才执行保存操作,校验失败时返回页面展示错误信息:

class PatientAddView(LoginRequiredMixin,TemplateView):
    model = Patient
    template_name = "../templates/patient/add.html"

    def get(self, *args, **kwargs):
        # 实例化表单而非直接传类
        patient_form = PatientForm()
        diagnosis_formset = DiagnosisFormSet(queryset=Diagnosis.objects.none())
        return self.render_to_response({
            'diagnosis_formset': diagnosis_formset,
            'patient_form': patient_form
        })

    def post(self, *args, **kwargs):
        form = PatientForm(data=self.request.POST)
        diagnosis_formset = DiagnosisFormSet(data=self.request.POST)

        # 两个表单同时校验通过才执行保存
        if form.is_valid() and diagnosis_formset.is_valid(): 
            patient_instance = form.save(commit=False)
            patient_instance.user = self.request.user
            patient_instance.save()

            diag_instances = diagnosis_formset.save(commit=False)
            for diag in diag_instances:
                diag.patient = patient_instance
                diag.save()
            # 仅保存成功后跳转
            return redirect(reverse(
                'patient:treatment_detail',
                kwargs={"patient_id": patient_instance.patient_id}
            ))
        # 校验失败返回带错误信息的表单页
        return self.render_to_response({
            'diagnosis_formset': diagnosis_formset,
            'patient_form': form
        })

3. 前端适配注意

  • 动态新增formset行时,必须正确更新id_form-TOTAL_FORMS等表单集管理字段的值,否则新增行不会被后端识别、不会触发校验。
  • 模板中需要渲染diagnosis_formset.non_form_errors,才能显示上面自定义的全局校验提示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 16:48:03