Django表单集:让第一个必填?

38 投票
4 回答
19100 浏览
提问于 2025-04-15 20:11

这些表单组的表现跟我想要的完全相反。

我的视图设置是这样的:

def post(request): # TODO: handle vehicle formset
    VehicleFormSetFactory = formset_factory(VehicleForm, extra=1)
    if request.POST:
        vehicles_formset = VehicleFormSetFactory(request.POST)
    else:
        vehicles_formset = VehicleFormSetFactory()

我的模板看起来是这样的:

    <div id="vehicle_forms">
        {{ vehicles_formset.management_form }}
        {% for form in vehicles_formset.forms %}
            <h4>Vehicle {{forloop.counter}}</h4>
            <table>
                {% include "form.html" %}
            </table>
        {% endfor %}
    </div>

这样一来,最开始只生成一个表单,正是我想要的。但是我希望这个表单是必填的!

当我用JavaScript动态添加空表单时,使用vehicles_formset.empty_form,所有这些额外的表单都是必填的,这正是我不想要的。

根据文档:

这个表单组足够聪明,可以忽略那些没有被修改的额外表单。

这是第一个表单的表现(这不是我想要的),但额外表单的表现却是我想要的。

有没有什么属性可以改变,让至少有一个表单变成必填的?

4 个回答

10

嗯……这就让第一个表单变成了必填项。

class RequiredFormSet(BaseFormSet):
    def clean(self):
        if any(self.errors):
            return
        if not self.forms[0].has_changed():
            raise forms.ValidationError('Please add at least one vehicle.') 

唯一的问题是,如果没有表单,那么clean这个方法似乎根本不会被调用,所以我不知道怎么检查有没有表单。其实……这种情况不应该发生(除了我的JavaScript有个bug,允许你删除所有的表单)。

29

Django 1.7 新增了一个功能:你可以通过 formset_factory 来指定这个行为。

点击这里查看详细文档

VehicleFormSetFactory = formset_factory(VehicleForm, min_num=1, validate_min=True, extra=1)
76

找到了一个更好的解决方案:

class RequiredFormSet(BaseFormSet):
    def __init__(self, *args, **kwargs):
        super(RequiredFormSet, self).__init__(*args, **kwargs)
        for form in self.forms:
            form.empty_permitted = False

然后像这样创建你的表单集合:

MyFormSet = formset_factory(MyForm, formset=RequiredFormSet)

真的不知道为什么一开始没有这个选项……不过,随便吧。花了我几个小时才搞明白。

这样做会让所有的表单都是必填的。如果你只想让第一个表单必填,可以把self.forms[0].empty_permitted设置为False

撰写回答