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

如何阻止Django Formset保存特定的部分填写表单

Django Formset 过滤仅填充Activity的空表单保存问题

方法一:表单级验证(推荐)

在WeeksForm中添加clean方法,直接在表单层面做校验,确保只有activity但无星期数据的表单无法通过验证:

class WeeksForm(forms.ModelForm):
    monday = forms.DecimalField(required=False)
    tuesday = forms.DecimalField(required=False)
    wednesday = forms.DecimalField(required=False)
    thursday = forms.DecimalField(required=False)
    friday = forms.DecimalField(required=False)
    saturday = forms.DecimalField(required=False)
    sunday = forms.DecimalField(required=False)

    class Meta:
        model = Weeks
        fields = ('activity', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday')

    def clean(self):
        cleaned_data = super().clean()
        # 定义所有星期字段的名称列表
        day_fields = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']
        # 检查是否有任意一个星期字段有值
        has_day_data = any(cleaned_data.get(day) is not None for day in day_fields)
        
        # 如果activity已填写,但所有星期字段为空,抛出验证错误
        if cleaned_data.get('activity') and not has_day_data:
            raise forms.ValidationError("请至少填写一个星期的数值")
        
        return cleaned_data

当用户提交仅含activity的表单时,formset.is_valid()会返回False,页面会显示对应的错误提示,阻止这类表单被保存。

方法二:视图层标记无效表单为删除状态

如果希望不显示错误提示,直接忽略这类表单,可以在视图中遍历表单,将符合条件的表单标记为删除(利用formset的can_delete特性):

if request.method == 'POST':
    formset = WeeksFormset(request.POST)
    if formset.is_valid():
        day_fields = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']
        for form in formset.forms:
            # 检查当前表单是否仅填写了activity,星期字段全为空
            has_day_data = any(getattr(form.instance, day) is not None for day in day_fields)
            if form.instance.activity and not has_day_data:
                # 标记为删除,save()时会跳过该表单
                form.cleaned_data['DELETE'] = True
            else:
                form.instance.user = request.user
        # 保存有效表单
        formset.save()
        return HttpResponseRedirect(reverse("time"))

方法三:表单集级验证

自定义表单集基类,在表单集层面统一处理校验逻辑:

from django.forms import BaseModelFormSet

class BaseWeeksFormSet(BaseModelFormSet):
    def clean(self):
        super().clean()
        day_fields = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']
        for form in self.forms:
            # 跳过本身就无效的表单
            if not form.is_valid():
                continue
            has_day_data = any(form.cleaned_data.get(day) is not None for day in day_fields)
            if form.cleaned_data.get('activity') and not has_day_data:
                form.add_error(None, "请至少填写一个星期的数值")

# 用自定义的表单集基类生成formset
WeeksFormset = modelformset_factory(Weeks, form=WeeksForm, formset=BaseWeeksFormSet, extra=10, can_delete=True)

这种方式和方法一类似,只是把校验逻辑放在了表单集层面,适合需要统一处理多个表单校验规则的场景。

内容的提问来源于stack exchange,提问作者Carlos S. C.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 05:10:22