Django:如何在表单集构建后添加额外的表单?

Posted

技术标签:

【中文标题】Django:如何在表单集构建后添加额外的表单?【英文标题】:Django: How to add an extra form to a formset after it has been constructed? 【发布时间】:2011-06-24 10:50:06 【问题描述】:

这大致就是我想要做的:

def post(request):
    VehicleFormSet = formset_factory(StaffVehicleForm)
    if request.method == 'POST':
        vehicle_formset = VehicleFormSet(request.POST)
        if 'add_vehicle' in request.POST:
            if vehicle_formset.is_valid():
                form_count = vehicle_formset.total_form_count()
                vehicle_formset.forms.append(vehicle_formset._construct_form(form_count))

基本上,如果用户单击“添加”按钮并且他们的条目有效,我想将另一个空白表单添加到表单集中,并隐藏前一个。

上面代码的问题是我不知道如何增加total_form_count()。我现在的方式,它会工作一次,然后如果你再次按下它,什么都不会发生,大概是因为form_count 是一样的。我也不喜欢打电话给_construct_form 依赖内部。

【问题讨论】:

【参考方案1】:
class RequiredFormSet(BaseFormSet):
    def add_form(self, **kwargs):
        # add the form
        tfc = self.total_form_count()
        self.forms.append(self._construct_form(tfc, **kwargs))
        self.forms[tfc].is_bound = False

        # make data mutable
        self.data = self.data.copy()

        # increase hidden form counts
        total_count_name = '%s-%s' % (self.management_form.prefix, TOTAL_FORM_COUNT)
        initial_count_name = '%s-%s' % (self.management_form.prefix, INITIAL_FORM_COUNT)
        self.data[total_count_name] = self.management_form.cleaned_data[TOTAL_FORM_COUNT] + 1
        self.data[initial_count_name] = self.management_form.cleaned_data[INITIAL_FORM_COUNT] + 1

    def add_fields(self, form, index):
        super(RequiredFormSet, self).add_fields(form, index)
        form.empty_permitted = False

这样就可以了。只用了7个小时就搞清楚了。而且我仍然不知道为什么我需要.is_bound = False 来使初始值不会搞砸。

【讨论】:

is_bound 应该始终为 False,除非您的表单绑定到提交的 POST(或获取)数据。 @Humphrey:是的……确实如此。整个表单集是。不过,我不希望绑定尚未添加的空白表单。 @mpen 我应该如何调用这个方法以及从哪里得到 TOTAL_FORM_COUNT 和其他变量。 @CrazyGeek 不知道。这是我 6 年前写的。对不起 感谢您提供时间提示,有时我觉得几行字就没有时间了,这让我很担心!【参考方案2】:

我使用 javascript 执行此操作。由于formset渲染了三个管理字段

<input type="hidden" id="id_TOTAL_FORMS" value="1" name="TOTAL_FORMS">
<input type="hidden" id="id_INITIAL_FORMS" value="1" name="INITIAL_FORMS">.
<input type="hidden" id="id_MAX_NUM_FORMS" name="MAX_NUM_FORMS">

您可以使用 javascript 来增加 id_TOTAL_FORMS 值,然后添加额外的字段。所以我会像这样创建我的字段集:

VehicleFormSet = modelformset_factory(StaffVehicleForm, extra = 0, max_num = None)

棘手的是在 javascript 中创建额外的表单字段。我通常使用 AJAX 从自定义视图中获取新行。

【讨论】:

BaseFormSet 上的 empty_form 属性可能在这里有用。 docs.djangoproject.com/en/1.2/topics/forms/formsets/#empty-form “返回一个前缀为 __prefix__ 的表单实例,以便更轻松地在带有 JavaScript 的动态表单中使用”我认为您可以将其作为模板进行克隆。 哦,好的提示 Reiner Gerecke!下次我肯定会使用它:-) @Reiner:如果绑定了表单集,则 empty_form 不尊重表单字段的默认值......一个相当烦人的错误(表单将完全空白,而不是包含默认值)。跨度> 我如何调用我的视图并要求它向我发送一个额外的表单,因为我的表单包含几个下拉字段和很多数据库绑定选项?您能告诉我们您是如何使用 AJAX 从自定义视图中获取一行的吗?谢谢! 这对我有帮助:***.com/questions/501719/…【参考方案3】:

对于后代来说,这是另一种在没有 JS(或与 JS 一起)的情况下工作的方式,它不需要深入了解 formset 方法。相反,您可以只检查 POST 数据并对其进行调整,就好像 JS 在客户端完成了一些工作一样。以下确保在表单集的末尾始终(至少)有一个空表单:

def hsview( request):
    HS_formset = formset_factory( HSTestForm, extra=3 )
    prefix='XYZZY'
    testinpost, empty = 'key', ''  # field in the form and its default/empty value
    extra=3

# I prefer to do the short init of unbound forms first, so I invert the usual test ...   
    if request.method != 'POST':

        formset = HS_formset( prefix=prefix)
    else:
       # process POSTed forms data. 
       # pull all relevant things out of POST data, because POST itself is not mutable
        # (it doesn't matter if prefix allows in extraneous items)

        data =  k:v for k,v in request.POST.items() if k.startswith(prefix)  

        #if there are no spare empty forms, tell it we want another form, in place of or extra to client-side JS
        #don't want to crash if unvalidated POST data is nbg so catch all ...
        try:
            n = int( data[ prefix + '-TOTAL_FORMS']) 
            test = '--'.format(prefix, n-1, testinpost)
            #print(test)
            test = data.get( test, empty)
        except Exception:
            test = 'bleagh'
            # log the error if it matters enough ...
        if test != empty: 
            data[ prefix + '-TOTAL_FORMS'] = n + 1 

        # now the usual formset processing ...
        formset = HS_formset( data, prefix=prefix)
        # other_form = OtherForm( request.POST)
        if formset.is_valid(): 
            ...            

【讨论】:

【参考方案4】:

我在 Vue.js 方法中使用 RegEx:

addForm: function () 
    this.count++
    let form_count = this.count
    form_count++

    let formID = 'id_form-' + this.count
    incremented_form = this.vue_form.replace(/form-\d/g, 'form-' + this.count)
    this.formList.push(incremented_form)
    this.$nextTick(() => 
        let total_forms = document.getElementsByName('form-TOTAL_FORMS').forEach
        (function (ele, idx) 
            ele.value = form_count
        )
    )
,

delForm: function () 
    if (this.count != 0) 
        this.count--
        let form_count = this.count
        form_count++

        let formID = 'id_form-' + this.count
        this.formList.pop()
        this.$nextTick(() => 
            let total_forms = document.getElementsByName('form-TOTAL_FORMS').forEach
            (function (ele, idx) 
                ele.value = form_count
            )
        )
    
    else return
,

【讨论】:

以上是关于Django:如何在表单集构建后添加额外的表单?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 django 注册表单中添加额外的字段?

如何在 Django 表单的输入中添加额外的属性?

Django Formset.is_valid() 额外表单失败

如何在表单提交之前将多个图像异步添加到 django 表单

提交时应该如何收集克隆的 Django 表单?

如何将额外的参数传递给 django admin 自定义表单?