除非选中,否则带有 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:
如果您想在表单中包含一个布尔值,可以是
True
或False
(例如选中或未选中的复选框),您必须记住在以下情况下传入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 会失败
带有public static void main方法的类,其中的成员变量必须是static的,否则main方法没法调用。除非是main里的局部变量。因为main方法就是static的啊。