除非选中,否则带有 BooleanField 的 Django 表单始终无效

Posted

技术标签:

【中文标题】除非选中,否则带有 BooleanField 的 Django 表单始终无效【英文标题】:Django form with BooleanField always invalid unless checked 【发布时间】:2012-01-22 11:48:07 【问题描述】:

我有一个使用以下表单的应用程序:

class ConfirmForm(forms.Form):
    account_name = forms.CharField(widget=forms.HiddenInput)
    up_to_date = forms.BooleanField(initial=True)

我使用以下模板摘录中的表格:

<form class="confirmform" action="/foo/" enctype="multipart/form-data" method="post">
 confirm_form.up_to_date  Check if this data brings the account up to date.<br>
 confirm_form.account_name  <input type="submit" name="confirm" value="Confirm" />
</form>

我的观点使用以下基本代码结构:

if request.method == 'POST':
    #check for 'confirm' because I actually have multiple forms in this page
    if 'confirm' in request.POST:
        confirm_form = ConfirmForm(request.POST)
        if confirm_form.is_valid():
            #do stuff
        else:
            c['confirm_form'] = confirm_form
else:
    c['confirm_form'] = ConfirmForm('account_name':'provided_value')

有两点不对:

1) 即使我设置了 initial=True,但在页面加载时复选框未选中

2) 除非我选中复选框,否则表单始终无效。它仅针对 up_to_date 给出错误:“此字段是必需的。”

我已阅读this similar question,但他的解决方案不适用于我的项目。

那么……这是怎么回事?

编辑:

我更新了上面的代码以更忠实于我的实际代码。

问题 #1 是我的错,因为我在实例化表单时通过绑定数据覆盖了初始值。

问题 #2 我仍然在考虑一个问题。在up_to_date 上使用required=False 可以解决问题,但是使用默认小部件似乎不正确,BooleanField 可以是NULL(导致有效性检查失败)或True,但绝不是@ 987654330@.

【问题讨论】:

【参考方案1】:

在 Django 表单中,必须使用 required=False 创建布尔字段。

当未选中该复选框时,浏览器不会发送请求的 POST 参数中的字段。如果不指定该字段是可选的,当不在 POST 参数中时,Django 会将其视为缺失字段。

恕我直言,如果 Django 在默认情况下为布尔表单字段提供此行为,那就太好了。

(Yuji 已经在 cmets 中回答了这个问题,但作为答案会很有用)

【讨论】:

【参考方案2】:

initial=True 应该使用checked="checked" 呈现小部件,所以我看不到那里的问题.. 但表单无效的原因是因为默认情况下所有字段都是必需的,除非您另有指定。

Field.required¶ 默认情况下,每个 Field 类假定值为 必需的,所以如果你传递一个空值——要么是无,要么是空的 string ("") -- 然后 clean() 将引发 ValidationError 异常:

如果您希望复选框或任何其他值是可选的,您需要将required=False 传递给表单字段构造函数。

up_to_date = forms.BooleanField(initial=True, required=False) 
# no longer required..

【讨论】:

尽管如此,initial=True 没有任何作用。另外,为什么解决方案是使该字段可选? BooleanField 不能返回 false 吗? html 渲染是否带有“选中”?你重启服务器了吗?您可以在此时启动调试器 (import pdb; pdb.set_trace()) 以仔细检查初始属性是否设置正确? assert form.fields['up_to_date'].initial == True 这些将缩小我们的问题范围.. 至于为什么要使该字段可选? 1:文档是这样说的。但是 2:复选框是 HTML 元素,在“未选中”时不会提交数据。 False 值只能由缺少输入来确定。 虞姬:(1) 他们有吗? the docs for BooleanField 说不这样的话。 (2) 没错!我现在明白我需要做什么才能让它工作,但我不知道为什么设计者会在我们想要一个错误的结果时允许 is_valid() 失败。可能是文档应该告诉我们始终让 'required=False' 除非小部件或验证器被更改。 当你想要一个 False 结果时,为什么设计者会允许is_valid() 失败?我不明白这里的问题。在您当前的代码示例中,它根本不允许 False 结果引发验证错误。【参考方案3】:

根据official docs:

如果您想在表单中包含一个布尔值,可以是 TrueFalse(例如选中或未选中的复选框),您必须记住在以下情况下传入 required=False创建 BooleanField。

【讨论】:

【参考方案4】:

只是一种预感,但这可能是关于基础的 - 花了几个小时才意识到类似的东西只是关于基本的变量类型。

在我的例子中,在创建 FORM 之后(非常基本,见下文)

class para_selection(forms.Form):
    first=forms.BooleanField(initial=False, required=False)
    second=forms.BooleanField(initial=False, required=False)
    third=forms.BooleanField(initial=False, required=False)
    fourth=forms.BooleanField(initial=False, required=False)

...我正在使用 如果选择 == '真': .. 在我看来。但由于表单使用的是布尔值,正确的代码应该是: 如果选择为真:

第一行永远不会起作用,因为“==”不与布尔类型一起使用。希望不是这样,但我想在这里标记它,因为我一直在互联网上尝试和搜索,没有注意我使用的变量类型。

【讨论】:

【参考方案5】:

确保您没有在页面第一次加载时覆盖初始表单。

我最初并没有检查 request.GET 中是否有任何内容。页面第一次加载 request.GET 是 None,调用 SomeForm(request.GET) 实际上会清除初始值。

def some_view(request):

    form = SomeForm()

    if request.method == 'GET':
        form = SomeForm(request.GET)

添加检查以确保 request.GET 中有内容已修复此问题。

def some_view(request):

    form = SomeForm()

    if request.method == 'GET' and request.GET:
        form = SomeForm(request.GET)

【讨论】:

【参考方案6】:

如果您按照建议进行操作,您的 up_to_date 字段可能始终为 False。你永远不会知道这是因为用户想要那样,还是因为用户跳过了字段

我在法律要求的 OPT_IN 问题上遇到了同样的问题。在注册时,我需要让用户做出良心决定,允许我的系统向他们发送未经请求的电子邮件(或不发送)。我正在使用 Django-Allauth 和自定义用户数据库。

我将在下面分享我的解决方案,希望您可以根据自己的情况进行调整 - 我改编自 the DOCS。

forms.py:

# ExtraFieldsSignup as per https://***.com/a/12308807
class SignupFormExtraFields(forms.Form):
  CHOICES = (('1', 'YES, keep in touch',), ('2', 'No',))
  first_name = forms.CharField(max_length=30, label='Voornaam')
  last_name = forms.CharField(max_length=30, label='Achternaam')
  opt_in = forms.ChoiceField(widget=forms.Radioselect, choices=CHOICES)

  def signup(self, request, user):
    user.first_name = self.cleaned_data['first_name']
    user.last_name = self.cleaned_data['last_name']
    # Now we figure out what the user chose:
    if self.cleaned_data['opt_in'] == '1':
        user.opt_in = True
    else:
        user.opt_in = False
    user.save()

【讨论】:

以上是关于除非选中,否则带有 BooleanField 的 Django 表单始终无效的主要内容,如果未能解决你的问题,请参考以下文章

除非客户端和服务器使用相同的 Windows 身份,否则带有 sspi 的 WCF Net.tcp 会失败

使用 actionscript 取消选中组件中的复选框?

带有public static void main方法的类,其中的成员变量必须是static的,否则main方法没法调用。除非是main里的局部变量。因为main方法就是static的啊。

除非刷新,否则谷歌地图无法正确显示

除非我刷新,否则 jQuery mobile 中的 JavaScript 不起作用

除非调用 reloadData,否则图像无法正确加载