我在模型中有一个下拉列表,用户不应该更改所选的值。
我发现disabled
完全符合我的需要。然而,这有点奇怪:
第一次打开表单(GET)时,会选择该值,用户无法更改该值。这很棒:
但是,只要unrelated field
出现验证错误,并且POST将用户返回到同一表单,之前的信息就会丢失。禁用的外键 - 下拉列表不再包含任何值,并且非常恼人。
我做了一些研究并在stackoverflow上找到了一些东西,并且似乎在禁用了foreignkey-dropdown小部件时,根本没有发回任何数据。虽然可以覆盖验证,但不会像third answer here解释的那样为下拉字段抛出任何错误。但是,如果任何其他不相关的字段抛出错误,则数据会丢失,因为禁用的下拉列表从未在第一时间向POST发送任何数据。
这是一个棘手的情况。
有没有办法将视图中的数据传递给request.POST?或者你的建议是什么?我可以使用readonly
代替disabled
,这样可以使用,但用户可以更改下拉列表,这也很烦人。
有什么想法吗?非常感谢
修改
小修正:数据没有完全丢失。而是将选择错误地设置为初始虚拟值。
<select id="id_form-0-deal_type" name="form-0-deal_type" disabled="disabled">
<option selected="selected" value="">---------</option>
<option value="1">deal 1</option>
<option value="2">deal 2</option>
</select>
更新
弗朗西斯的解决方案看起来很有希望。所以我尝试了他的第二个建议,并在html中添加了一个隐藏的输入字段,并将正确的值传递给POST。
现在的问题是如何进行。我试图像这样在formset的表单的querydict中添加缺少的条目(为了设置正确的下拉值)
formset.forms[0].data['form-0-deal_type'] = formset.forms[0].data['form-0-hiddenfield']
但它说This QueryDict instance is immutable
唯一的另一种方法是使用常规表单集将其设置为Initials。不幸的是,我使用的是modelformsets,doesn't support initials用于现有表单。
如果没有其他解决方案,我会开始将我的modelformset重构为常规formset。仍然愿意接受......
最终更新+解决方案:
无需将modelformset重构为常规fomsets。事实上,我非常不鼓励这样做,因为它带来了其他问题。 modelformsets为您处理所有事情并填补缺失的部分。
实际问题是QueryDict是不可变的,但这可以通过复制来轻松解决:
formset = deal_formset(request.POST, queryset=formset_query)
if formset.is_valid():
pass
else:
new_post = request.POST.copy()
deal_types = dict()
for k,v in new_post.items():
if k.startswith('hidden'):
deal_types[k[7:]]= v
for k,v in deal_types.iteritems():
new_post[k] = v
formset = deal_formset(new_post, queryset=formset_query)
这加上弗朗西斯的解决方案:
{{ formset.management_form }}
{% for fs in formset %}
{{ fs.id }}
<input type="hidden" name="hidden-{{ fs.prefix }}-deal_type" value="{{fs.deal_type.value}}" />
{{fs.deal_type}}
{% endfor %}
{% endif %}
只是创造奇迹...享受:)
答案 0 :(得分:18)
它不是一个django的东西,它是一个HTML的东西。表单不会发送已禁用的表单元素。
[元素]无法接收用户输入,也无法使用表单提交其值。
http://www.w3.org/TR/html401/interact/forms.html#h-17.12.1&amp; http://www.w3schools.com/tags/att_input_disabled.asp
如果它位于text / textarea上,您可以使用readonly
http://www.w3schools.com/tags/att_input_readonly.asp
你可以做的其他事情是显示价值明文,并将其作为隐藏字段提交....
{{ form.field_name.label_tag }}
{{ form.field_name.value }}
<input type="hidden" name="field_name" value="{{form.field_name.value}}" />
它不是很优雅,但它可以帮助你。
您还可以更进一步,编写一些查找已禁用元素的JS,然后添加一个带有该元素名称和值的输入。
一些示例JQuery:
//Untested, but you get the gist
$(':disabled').each(
function()
{
$(this).after('<input type="hidden" name="' + $(this).attr('name') + '" value="' + $(this).val() + '" />');
}
);
答案 1 :(得分:1)
好吧,您可以在模板中使用hidden属性设置元素,使用视图中的formsets来构建表单:
{{form.field.as_hidden}}
并且在视图内部,如果问题是数据丢失,您可以始终为适合您的模型结构的字段设置初始值,因为它是外键。当然,您必须在提交之前验证表单,如果表单无效,您可以在必须始终填充的字段上使用初始值对其进行渲染。
答案 2 :(得分:0)
我认为这是一个HTML问题,而不是Django,禁用的表单字段不会发布其值,因此您将失去价值。
如果验证失败,是否可以将值重新绑定到字段?你可以试试像
这样的东西if form.is_valid(): # All validation rules pass
#save form, redirect, etc.
else:
form.disabled_field = my_value
return render(request, 'contact.html', {'form': form,})
显然,您需要使用模型中的正确数据替换字段名称和值。