Django 表单:如何让禁用字段在验证之间保持有效

7 投票
2 回答
4448 浏览
提问于 2025-04-15 15:13

在某个时候,我需要显示一个被标记为 "disabled"(通过 disabled="disabled" 属性使其变灰)的下拉选择框(类型为 "select")。根据标准(xhtml 和 html4),下拉选择框是不能使用 "readonly" 属性的。需要注意的是,这只是为了展示效果,实际的值还是需要在提交时包含在 POST 请求中。所以我这样做(引用了 django 中表单声明的一部分):

from django import forms

_choices = ['to be', 'not to be']
class SomeForm(forms.Form):
    field = forms.ChoiceField(choices=[(item, item) for item in _choices],
                   widget=forms.HiddenInput()) # the real field

    mock_field = forms.ChoiceField(required=False, # doesn't get submitted
                        choices=[(item, item) for item in _choices],
                        label="The question",
                        widget=forms.Select(attrs={'disabled':'disabled'}))

然后它是这样初始化的:

initial_val = 'to be'
form = SomeForm(ititial={'field':initial_val,
                         'mock_field':initial_val})

一切都很好。直到表单进行验证时,如果其他某个字段验证失败了。这时,表单会重新加载,之前填写的值会被保留,但“mock_field”的值却不会被保留——因为它是禁用的,根本没有被提交。所以它的值就没了。虽然这不影响数据的完整性,但在展示上看起来还是不太好。

有没有什么办法可以保留这个字段的值,尽量少做一些复杂的处理?这个表单是 django.contrib.formtools.FormWizard 的一部分,初始值(还有一些字段)是动态生成的。基本上,已经有很多事情在进行中,如果能不让事情变得更复杂,那就太好了。

2 个回答

3

浏览器不会提交被禁用的字段。

你可以尝试在表单的 __init__ 方法中把 field 的初始值复制到 mock_field

def __init__(self, *args, **kwargs):
    super(SomeForm, self).__init__(*args, **kwargs)
    mock_initial = self.fields['field'].initial
    self.fields['mock_field'].initial = mock_initial

这段代码没有经过测试。通常你还会关注 form.data,但在这种情况下,它和 initial 的值是一样的。

1

好吧,这是我第一次回答自己的问题,不过我找到了解决办法,虽然这确实有点像是个小窍门,但它有效。

我没有直接从表单实例中获取初始值,因为在构造函数里,self.fields['whatever'].initial 似乎是 None。相反,我是从关键字参数 "initial" 中获取这个值。然后我把它设置为 "mock" 字段的唯一选择。就像这样:

from django import forms

_choices = ['to be', 'not to be']
class SomeForm(forms.Form):
    field = forms.ChoiceField(choices=[(item, item) for item in _choices],
                   widget=forms.HiddenInput()) # the real field

    mock_field = forms.ChoiceField(required=False, # doesn't get submitted
                        choices=[(item, item) for item in _choices],
                        label="The question",
                        widget=forms.Select(attrs={'disabled':'disabled'}))

    def __init__(self, *args, **kwargs):
        super(SomeForm, self).__init__(*args, **kwargs)
        mock_initial = kwargs['initial']['field']
        self.fields['mock_field'].choices = [(mock_initial, mock_initial),]

这可能需要一些错误处理。显然,如果没有为实际的 field 提供初始值,这个方法就行不通了。

撰写回答