3

信息:

我想创建嵌套表单,如通过以下示例提供的最佳描述:

http://yergler.net/blog/2009/09/27/nested-formsets-with-django/

此页面上的教程似乎非常好 && 它试图解决我遇到的确切问题。

当没有 POST 请求数据时(即我们正在从数据库执行初始填充),views.py 文件中的此实现似乎存在问题。

代码可以在上面提供的 URL 中看到(如果需要,我可以发布一些代码,但我担心它会从这里提供的信息中删除)。

这是失败的views.py代码(粗体):

block = get_object_or_404(models.Block, id=block_id)

if request.method == 'POST':
    formset = forms.BuildingFormset(request.POST, instance=block)

    if formset.is_valid():
        rooms = formset.save_all()

        return redirect('block_view', block_id=block.id)

else:
    formset = forms.BuildingFormset(instance=block)  #This is the line that is throwing the ValidationError 

我收到的错误消息是:

ValidationError at "urlName":
[u'ManagementForm data is missing or has been tampered with']

我已经深入挖掘,看来这个失败发生在 site-packages/django/forms/formsets.py

检查失败,is_valid()因为一些管理表单所需的数据(form-TOTAL_FORMS、form-INITIAL_FORMS 和 form-MAX_NUM_FORMS)无效。下面是 self.errors 的实际输出:

{u'TOTAL_FORMS': [u'This field is required.'], u'INITIAL_FORMS': [u'This field is required.']}

代码:

edit_building.html:

{{ building.management_form }}

{% for building.forms %}

 {{ building }}

 {% if building.nested %}   
   {% for formset in building.nested %}   
     {{ formset.as_table }}   
   {% endfor %}   
 {% endif %}

{% endfor %}

视图.py:

def should_delete(self, form):
    """Convenience method for determining if the form’s object will
    be deleted; cribbed from BaseModelFormSet.save_existing_objects."""

    if self.can_delete:
        raw_delete_value = form._raw_value(DELETION_FIELD_NAME)
        should_delete = form.fields[DELETION_FIELD_NAME].clean(raw_delete_value)
        return should_delete

    return False

def save_all(self, commit=True):
    """Save all formsets and along with their nested formsets."""

    # Save without committing (so self.saved_forms is populated)
    # — We need self.saved_forms so we can go back and access
    #    the nested formsets
    objects = self.save(commit=False)

    # Save each instance if commit=True
    if commit:
        for o in objects:
            o.save()

    # save many to many fields if needed
    if not commit:
        self.save_m2m()

    # save the nested formsets
    for form in set(self.initial_forms + self.saved_forms):
        if self.should_delete(form): continue

        for nested in form.nested:
            nested.save(commit=commit)

表格.py:

def should_delete(self, form):
    """Convenience method for determining if the form’s object will
    be deleted; cribbed from BaseModelFormSet.save_existing_objects."""

    if self.can_delete:
        raw_delete_value = form._raw_value(DELETION_FIELD_NAME)
        should_delete = form.fields[DELETION_FIELD_NAME].clean(raw_delete_value)
        return should_delete

    return False

def save_all(self, commit=True):
    """Save all formsets and along with their nested formsets."""

    # Save without committing (so self.saved_forms is populated)
    # — We need self.saved_forms so we can go back and access
    #    the nested formsets
    objects = self.save(commit=False)

    # Save each instance if commit=True
    if commit:
        for o in objects:
            o.save()

    # save many to many fields if needed
    if not commit:
        self.save_m2m()

    # save the nested formsets
    for form in set(self.initial_forms + self.saved_forms):
        if self.should_delete(form): continue

        for nested in form.nested:
            nested.save(commit=commit)

笔记:

问题:

如果没有 POST 数据,并且表单是完全从数据库生成的,如何正确填写 'form-TOTAL_FORMS' && 'form-INITIAL_FORMS' 数据以解决此故障?

4

1 回答 1

5

更新:

查看您提供的示例forms.py后,在方法的末尾有一个如下所示的片段add_fields()

# store the formset in the .nested property
form.nested = [
    TenantFormset(data = self.data,
                  instance = instance,
                  prefix = 'TENANTS_%s' % pk_value)
]

data参数导致问题,因为它最初是空的,并且在内部 Django 将确定表单是否受类似于此的条件约束:

self.is_bound = data is not None

# Example
>>> my_data = {}
>>> my_data is not None
True

正如您所见,Python 中的空字典不是 None,因此即使它不是,您TenantFormset也会被视为一种形式。bound您可以使用以下方法修复它:

# store the formset in the .nested property
form.nested = [
    TenantFormset(data = self.data if any(self.data) else None,
                  instance = instance,
                  prefix = 'TENANTS_%s' % pk_value)
]

您能否发布视图和表单代码以及表单的模板代码?

我的猜测是您没有在模板中使用“management_form”(它添加了您缺少的“form-TOTAL_FORMS”和“form-INITIAL_FORMS”字段),即

<form method="post">
    {{ formset.management_form }}
    <table>
        {% for form in formset %}
        {{ form }}
        {% endfor %}
    </table>
</form>
于 2013-07-08T01:42:21.253 回答