【发布时间】:2012-07-29 17:49:20
【问题描述】:
我在模型表单中有一个下拉菜单,用户应该无法更改所选值。
我发现 disabled 确实可以满足我的需要。然而,这有一个奇怪的地方:
第一次打开表单 (GET) 时选择了该值,用户无法更改该值。这很棒:
但是,一旦unrelated field 出现验证错误并且 POST 将用户发送回相同的表单,之前的信息就会丢失。禁用的外键下拉列表不再包含任何值,非常烦人。
我做了一些研究,并在 stackoverflow 上发现了一些东西,似乎当禁用外键下拉小部件时,根本没有数据被发回。正如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>
更新:
Francis 的解决方案看起来很有希望。所以我尝试了他的第二个建议,并在 html 中添加了一个隐藏的输入字段,并将正确的值传递给 POST。
现在的问题是如何进行。我试图像这样在表单集的表单查询字典中添加缺少的条目(以便设置正确的下拉值)
formset.forms[0].data['form-0-deal_type'] = formset.forms[0].data['form-0-hiddenfield']
但上面写着This QueryDict instance is immutable
唯一的其他方法是通过Initials 使用常规表单集进行设置。不幸的是,我正在使用模型表单集,doesn't support initials 用于现有表单。
如果没有其他解决方案,我开始将我的模型表单集重构为常规表单集。仍然开放的想法...
最终更新 + 解决方案:
没有必要将 modelformset 重构为常规 fomset。事实上,我非常不鼓励这样做,因为它本身会带来其他问题。 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 %}
只是创造奇迹......享受:)
【问题讨论】:
-
"数据并没有完全丢失。而是选择错误地设置为初始虚拟值。" - 那就是那个字段没有被张贴的b/c。所以使用默认值