[使用Formset前缀时缺少ManagementForm数据

问题描述 投票:2回答:3

任何人都知道为什么当我使用Formset Prefix时,这会导致缺少ManagementForm数据吗?

来自Shell

>>> from django import forms
>>> from django.forms.formsets import formset_factory
>>> 
>>> class CheckBox (forms.Form):
...     overwrite = forms.BooleanField (required = False)
... 
>>> 
>>> data = {
...     'form-TOTAL_FORMS': '2',
...     'form-INITIAL_FORMS': '0',
...     'form-MAX_NUM_FORMS': '3',
...     'checkbox-0-overwrite': True,
...     'checkbox-1-overwrite': False,
... }
>>> 
>>> CheckBoxFormSet = formset_factory (CheckBox)
>>> formset = CheckBoxFormSet (data)
>>> formset.is_valid ()
True
>>> formset.cleaned_data
[{}, {}]
>>> 

向表单集添加前缀

>>> formset = CheckBoxFormSet (data, prefix = 'checkbox')
>>> formset.is_valid ()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
.
.
.
django.core.exceptions.ValidationError: ['ManagementForm data is missing or has been tampered with']

[Django Doc提到使用前缀在'a'视图中区分不同的格式集。如果我在同一视图中以不同的方法(如示例)处理不同的HTML页面使用它,是否适用? Django在示例中建议的操作也会触发ManagementForm数据丢失错误。

例如:

forms.py

class NodeForm (forms.Form):

    cars = forms.CharField (required = False)
    trucks = forms.CharField (required = False)

class CheckBox (forms.Form):
    overwrite = forms.BooleanField (required = False)

views.py

def cars (request):

    CarsFormSet = formset_factory (CarsForm, formset = BaseNodeFormSet, extra = 2, max_num = 5)

    if request.method == 'POST':

        cars_formset = CarsFormSet (request.POST, prefix = 'carsform')

        if cars_formset.is_valid ():
            data = cars_formset.cleaned_data

            context = {'data': data}
            return render (request, 'vehicleform/response.html', context)
        else:
            cars_formset = CarsFormSet (prefix = 'carsform')

     context = {...previously entered data from POST...}

     return render (request, 'vehicleform/carsform.html', context)

def trucks (request):

    TrucksFormSet = formset_factory (TrucksForm, extra = 2, max_num = 5)

    if request.method == 'POST':

        trucks_formset = TrucksFormSet (request.POST, prefix = 'trucksform')

        if trucks_formset.is_valid ():
            data = truck_formset.cleaned_data

            context = {'data': data}
            return render (request, 'vehicleform/success.html', context)

        else:
            trucks_formset = TrucksFormSet (prefix = 'trucksform')

     return HttpResponse ('No overwrite data.')

更新1我将其范围缩小到实际数据。由于某些原因,它不喜欢我的数据。

更新2我已经验证了表格中的名称和数据是否相同。当我在数据中声明2时,它仅打印一个复选框-0覆盖。想知道为什么表单集不能用于复选框。

>>> CheckBoxFormSet = formset_factory (CheckBox)
>>> formset = CheckBoxFormSet (prefix = 'checkbox')
>>> 
>>> for form in formset:
...     print (form)
... 
<tr><th><label for="id_checkbox-0-overwrite">Overwrite:</label></th><td><input id="id_checkbox-0-overwrite" name="checkbox-0-overwrite" type="checkbox" /></td></tr>
>>> 

更新3我不确定发生了什么。这似乎生成不带前缀的表格。插入前缀后仍然出现错误。

>>> CheckBoxFormSet = formset_factory (CheckBox)
>>> formset = CheckBoxFormSet (data)
>>> formset.is_valid ()
True
>>> for form in formset:
...     print (form)
... 
<tr><th><label for="id_form-0-overwrite">Overwrite:</label></th><td><input id="id_form-0-overwrite" name="form-0-overwrite" type="checkbox" /></td></tr>
<tr><th><label for="id_form-1-overwrite">Overwrite:</label></th><td><input id="id_form-1-overwrite" name="form-1-overwrite" type="checkbox" /></td></tr>
>>> 
>>> 
>>> data {
...    'form-TOTAL_FORMS': '2',
...    'form-INITIAL_FORMS': '0',
...    'form-MAX_NUM_FORMS': '3',
...    'checkbox-0-overwrite': True
}

更新4下面的html模板是由第一种形式(汽车)生成和创建的,正如我从上面的示例中更新的那样。第二种形式仅在第一种形式传递的数据旁边插入复选框。在模板中显示表单集并单击“提交”仍然给我该“ ManagementForm”错误。我将尝试仅使用复选框创建一个全新的表单,以查看是否给我任何错误。

Response.html

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">
<head lang="en">
    <meta charset="UTF-8">
    {% load staticfiles %}
    <link rel="stylesheet" type="text/css" href="{% static 'nodeform/style.css' %}" >
    <title>Vehicle Information</title>
</head>
<body>
    <h1>Vehicle Information:</h1>
    <h4>Location: {{ location }}</h4>
    <form action="trucks" method="POST">{% csrf_token %}
    {{ checkbox_formset.management_form }}
       {% for form in checkbox_formset %}
        {{ form }}
       {% endfor %}
    <br>
    <p><input type="submit" value="Confirm">
    <a href="{% url 'carsform' %}">
        <button type="button">Cancel</button></a></p>
    </form>
</body>
</html>

更新5我不确定我是否理解正确,但是我认为失败是在表单的动作以及如何获取数据之内。初始表单(carsform.html)的表单标签没有任何操作:

carsform.html

<form action="" method="POST">{% csrf_token %}...</form>

它执行POST,然后将收集到的信息传递到下一页/表单(response.html)。另外,它向以前的数据添加复选框的表单集,如下所示:

response.html

<form action="trucks" method="POST">{% csrf_token %}...</form>

输出:

Audi (Obtained from cars)    []  <---Checkbox inserted from response.html manually & obtaining data from method trucks
Toyota (Obtained from cars)  []  <---Checkbox inserted from response.html manually & obtaining data from method trucks

[当用户点击“提交”时,response.html表单将进行处理,并将自身再次“撤消”回卡车。这次没有来自cars方法的数据要处理。这最终引发了ManagementForm错误。

我已经通过在初始页面(carsform.html)中插入2个表单集并单击提交来进行测试。我在下一页/表单(response.html)上看到的结果同时包含第一个和第二个表单集的数据。

我的下一个问题是如何创建第二种形式(response.html)以获取没有错误的数据?

python django django-forms django-views formset
3个回答
4
投票

根据docs

重要的是,您需要在两个POST和非POST情况,以便呈现和处理正确。

因此,首先,当呈现空白表单(即,不是POST)时,您将具有:

trucks_formset = TrucksFormSet(prefix ='trucksform')

同样,您的“数据”以前缀失败,因为您没有更改数据中字段的名称。前缀重命名您的字段。您可以尝试将表单集发布到模板上,然后会看到隐藏字段的名称。

 data = {
...     'checkbox-TOTAL_FORMS': '2',
...     'checkbox-INITIAL_FORMS': '0',
...     'checkbox-MAX_NUM_FORMS': '',
...     'checkbox-0-overwrite': True,
...     'checkbox-1-overwrite': False,
... }
>>> 
>>> CheckBoxFormSet = formset_factory (CheckBox, extra=1)
>>> formset = CheckBoxFormSet (data, prefix = 'checkbox')

2
投票

问题在于方法汽车渲染到response.html页面并在URL http://..../vehicle/cars而不是车辆/卡车上显示渲染的表单。出现“管理表单错误”的原因是,“ POST”是第二次发生,但仍以网址车辆/汽车形式而不是车辆/卡车形式出现。更新5暗示了问题。解决方法是简单地使用

return HttpResponseRedirect ('trucks')

render (request, 'vehicleform/trucksform.html', context)
return HttpResponseRedirect ('trucksform')

[2之间的区别是,第一种解决方案从第二种形式(truckform)呈现数据,而第二种解决方案从第一种形式(carsform)呈现数据。

为什么这么重要?好吧,因为我希望第一个表单重新显示自身,如果有错误,则不会重定向到另一页;因此,

<form action="" method="POST">

否则,设置

<form action="truck" method="POST">

不会创建这个烂摊子。

为了能够在单个视图中使用2个不同的表单集,请转到其直接URL分别测试每个页面/表单。一旦确认两个页面均已呈现并按预期工作,请使用HttpResponseRedirect。

感谢onyeka的所有帮助。


0
投票

这不完全是OP的问题,但这是我搜索此错误时出现的第一个结果,因此我认为我将与前缀(包括在内)共享问题,因为这并不明显。] >

Django表单框架automatically defines _id_for_label,如果您未将其设置为表单字段的属性,但未将其从表单集数据中删除。因此,如果您的标签为“ my_label”,则将其呈现为“ id_my_label”,并像这样返回POST数据。

[花了我一段时间才在我的request.POST(下面的data)中发现了这个]

MyFormSet = formset_factory(MyForm, prefix='my_form')

data = {
    'id_result_form-MAX_NUM_FORMS': '1000', # note how django added 'id_'
    'id_result_form-INITIAL_FORMS': '0',
    'id_result_form-TOTAL_FORMS': '1',
}

formset = MyFormSet(data, prefix='my_form')
formset.is_valid()
>>> False

formset = MyFormSet(data, prefix='id_my_form') # added 'id_'
formset.is_valid()
>>> True
© www.soinside.com 2019 - 2024. All rights reserved.