当我使用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
>>> 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提到使用前缀来区分&#39; a&#39;视图。如果我在同一个视图中使用它,但是在处理不同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
我已经验证了表单中的名称和数据是否相同。它只打印一个复选框-0-覆盖,而我在我的数据中说明了2。不知道为什么formset不适用于复选框。
>>> 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模板是由第一个表单cars生成并创建的,正如我从上面的示例中更新的那样。第二种形式,只插入第一个表单传递的数据旁边的复选框。在模板中显示formset并单击Submit仍然会给我&#34; ManagementForm&#34;错误。我将尝试使用复选框创建一个全新的表单,以查看是否会给我带来任何错误。
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
当用户点击&#34;提交&#34;时,response.html表单将会处理并且#34;反向&#34;本身又回到了卡车上。这次没有来自汽车方法的数据来处理。这最终引发了一个ManagementForm错误。
我已经通过在初始页面(carsform.html)中插入2个表单集并单击“提交”对此进行了测试。我在下一页/表单(response.html)上看到的结果包含第一个和第二个表单集的数据。
我的下一个问题是如何创建第二个表单(response.html)以获取没有错误的数据?
答案 0 :(得分:2)
根据docs:
重要的是要指出你需要在两者上传递前缀 POST和非POST情况,以便进行渲染和处理 正确。
所以,首先,当渲染空白表格(即不是POST)时,您将拥有:
trucks_formset = TrucksFormSet(prefix ='trucksform')
您的数据&#39;没有使用前缀,因为您没有更改数据中字段的名称。前缀重命名您的字段。您可以尝试将formset发布到模板,然后您将看到隐藏的字段&#39;名。
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')
答案 1 :(得分:2)
问题在于方法汽车呈现到response.html页面并在网址http://..../vehicle/cars而不是车辆/卡车上显示呈现的表单。提出“管理表格错误”是因为“POST”发生了第二次,同时仍处于车辆/车辆形式,而不是车辆/卡车形式。更新5暗示了问题。解决方案是简单地使用
return HttpResponseRedirect ('trucks')
或
render (request, 'vehicleform/trucksform.html', context)
return HttpResponseRedirect ('trucksform')
上述2之间的差异是第一个解决方案从第二个表单(trucksform)呈现数据,而第二个解决方案从第一个表单(carsform)呈现数据。
为什么这么重要?好吧,因为我希望第一个表单重新显示自己,如果有错误,不重定向到另一个页面;因此,
<form action="" method="POST">
否则,设置
<form action="truck" method="POST">
不会造成这种混乱。
为了能够在一个视图中使用2个不同的表单集,请通过转到其直接URL单独测试每个页面/表单。一旦确认两个页面都呈现并按预期工作,请使用HttpResponseRedirect。
感谢onyeka提供的帮助。
答案 2 :(得分: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