django - 必须至少填入一个表单字段
我正在学习使用Django 1.4(等1.7发布后我会升级)。
我有一个测试表单,里面所有的字段都设置为可选(required=False),但我希望用户至少填写一个字段。填写哪个字段都没关系,只要有一个字段被填写就可以。
我现在有一个可行的解决方案,但我觉得可以做得更好,所以希望有更懂Django的人能给我提供一个更好的解决办法。
这是我的模型代码:
....
nationality_visa_country_of_birth = models.TextField(null=True, blank=True)
nationality_visa_citizenship = models.TextField(null=True, blank=True)
nationality_visa_residency = models.TextField(null=True, blank=True)
nationality_visa_work_visa = models.TextField(null=True, blank=True)
nationality_visa_study_visa = models.TextField(null=True, blank=True)
nationality_visa_specialist_visa = models.TextField(null=True, blank=True)
nationality_visa_other_visa = models.TextField(null=True, blank=True)
nationality_visa_current_valid_passport_display_type = models.PositiveIntegerField(default=0)
nationality_visa_multiple_passports = models.BooleanField(default=False)
nationality_visa_passport_nationality = models.TextField(null=True, blank=True)
nationality_visa_timestamp_added = models.DateTimeField(auto_now_add=True, auto_now=False)
nationality_visa_timestamp_updated = models.DateTimeField(auto_now=True, auto_now_add=False)
....
这是我的表单代码:
nationality_visa_country_of_birth = forms.CharField(label=_('Country of Birth'), max_length=250, required=False)
nationality_visa_citizenship = forms.CharField(label=_('Citizenship'), max_length=250, required=False)
nationality_visa_residency = forms.CharField( label=_('Residency'), max_length=250, required=False)
nationality_visa_work_visa = forms.CharField( label=_('Work Visa'), max_length=250, required=False)
nationality_visa_study_visa = forms.CharField( label=_('Student Visa'),max_length=250, required=False)
nationality_visa_specialist_visa = forms.CharField(label=_('Specialist Visa'), max_length=250, required=False)
nationality_visa_other_visa = forms.CharField(label=_('Other Visa'), max_length=250, required=False)
nationality_visa_current_valid_passport_display_type = forms.TypedChoiceField(coerce=int, label=_('Current Valid Passport'), choices=[(x, x) for x in range(0, 3)], required=False)
nationality_visa_multiple_passports = forms.BooleanField( label=_('Multiple Passports'), required=False, help_text=_("Select this option if you're going to enter multiple passport details into the field below."))
nationality_visa_passport_nationality = forms.CharField( label=_('Nationality of Passport(s)'), max_length=250, required=False)
这是在forms.py中的验证代码,它会要求用户至少填写一个字段:
def clean(self):
cd_nvdf = super(NationalityVisaDetailsForm, self).clean()
if 'nationality_visa_country_of_birth' in cd_nvdf and len(cd_nvdf['nationality_visa_country_of_birth'].strip()) == 0:
if 'nationality_visa_citizenship' in cd_nvdf and len(cd_nvdf['nationality_visa_citizenship'].strip()) == 0:
if 'nationality_visa_residency' in cd_nvdf and len(cd_nvdf['nationality_visa_residency'].strip()) == 0:
if 'nationality_visa_work_visa' in cd_nvdf and len(cd_nvdf['nationality_visa_work_visa'].strip()) == 0:
if 'nationality_visa_study_visa' in cd_nvdf and len(cd_nvdf['nationality_visa_study_visa'].strip()) == 0:
if 'nationality_visa_specialist_visa' in cd_nvdf and len(cd_nvdf['nationality_visa_specialist_visa'].strip()) == 0:
if 'nationality_visa_other_visa' in cd_nvdf and len(cd_nvdf['nationality_visa_other_visa'].strip()) == 0:
if 'nationality_visa_current_valid_passport_display_type' in cd_nvdf and cd_nvdf['nationality_visa_current_valid_passport_display_type'] == 0:
if 'nationality_visa_passport_nationality' in cd_nvdf and len(cd_nvdf['nationality_visa_passport_nationality'].strip()) == 0:
self._errors['nationality_visa_country_of_birth'] = self.error_class([_("You must enter at least one Nationality & Visa Detail.")])
self._errors['nationality_visa_citizenship'] = self.error_class([_(" ")])
self._errors['nationality_visa_residency'] = self.error_class([_(" ")])
self._errors['nationality_visa_work_visa'] = self.error_class([_(" ")])
self._errors['nationality_visa_study_visa'] = self.error_class([_(" ")])
self._errors['nationality_visa_specialist_visa'] = self.error_class([_(" ")])
self._errors['nationality_visa_other_visa'] = self.error_class([_(" ")])
self._errors['nationality_visa_current_valid_passport_display_type'] = self.error_class([_(" ")])
self._errors['nationality_visa_passport_nationality'] = self.error_class([_(" ")])
del self.cleaned_data['nationality_visa_country_of_birth']
del self.cleaned_data['nationality_visa_citizenship']
del self.cleaned_data['nationality_visa_residency']
del self.cleaned_data['nationality_visa_work_visa']
del self.cleaned_data['nationality_visa_study_visa']
del self.cleaned_data['nationality_visa_specialist_visa']
del self.cleaned_data['nationality_visa_other_visa']
del self.cleaned_data['nationality_visa_current_valid_passport_display_type']
del self.cleaned_data['nationality_visa_passport_nationality']
return cd_nvdf
目前,经过验证的表单会高亮显示上述表单字段,但在模板中的每个表单字段下方,都会插入以下HTML代码,导致每个表单字段下面多出一行空白:
<span class="help-inline">
<strong>
<br>
</strong>
</span>
我该如何更好地编写验证代码,以确保用户至少填写一个表单字段,或者不包括上面显示的额外换行代码呢?
2 个回答
这里有一些Django的细节可能会对你有帮助,但我建议你先考虑Python的部分。
对于第一个任务,我觉得你可以用一个filter
来简化操作。
首先,我们需要一个你想要检查的值的列表:
values_we_care_about = ['nationality_visa_country_of_birth'
'nationality_visa_citizenship'
'nationality_visa_residency'
'nationality_visa_work_visa',
etc.]
接下来,我们需要把你用来检查的逻辑提炼成一个简单的函数。
def checking_stuff_function(value_cared_about):
return value_cared_about in cd_nvdf and cd_nvdf.get(value_cared_about, None)
现在,我们可以用这个函数来过滤所有的值(如果你用的是Python2,这会直接返回一个列表,但如果你用的是Python3,你会得到一个生成器;我假设你用的是Python2):
checked_values = filter(checking_stuff_function, values_we_care_about)
现在,我们可以检查有多少个checked_values
通过了检查。你可能已经能猜到接下来要做什么了……
if len(checked_values) < 1:
# There's an error!
现在,你只需要重置你的表单(因为我们之前用了filter,为什么不在这里用map
呢?):
map(lambda val: del self.cleaned_data[val], values_we_care_about]
不过,我从来没有在表单上这样做过。真的有必要在重写的clean方法中删除它们吗?难道在视图中处理会更好吗?这个问题我就留给别人回答吧。
之后你可以直接返回你的表单。
编辑
我忘了提到你的错误。处理错误的方式也可以和我们之前做的类似,所以我就不详细说明了。
基本上,你原来的代码缺少了一个原则:DRY原则,也就是“不要重复自己”。试着找出你在做的事情中有哪些共同的模式,并利用这些模式。如果你在三四个地方写了同样的字符串数组,那就是个问题。把它们保存到一个列表中,然后遍历这个列表就好了。这样代码会简洁很多。
这里是一个可行的解决方案:
def clean(self):
cd_nvdf = super(NationalityVisaDetailsForm, self).clean()
if not any(
cd_nvdf.get(x, '')
for x in (
'nationality_visa_country_of_birth',
'nationality_visa_citizenship',
'nationality_visa_residency',
'nationality_visa_work_visa',
'nationality_visa_study_visa',
'nationality_visa_specialist_visa',
'nationality_visa_other_visa',
'nationality_visa_current_valid_passport_display_type',
'nationality_visa_passport_nationality',
)
):
self._errors['nationality_visa_country_of_birth'] = self.error_class([_("You must enter at least one Nationality & Visa Detail.")])
self._errors['nationality_visa_citizenship'] = self.error_class([_("You must enter at least one Nationality & Visa Detail.")])
self._errors['nationality_visa_residency'] = self.error_class([_("You must enter at least one Nationality & Visa Detail.")])
self._errors['nationality_visa_work_visa'] = self.error_class([_("You must enter at least one Nationality & Visa Detail.")])
self._errors['nationality_visa_study_visa'] = self.error_class([_("You must enter at least one Nationality & Visa Detail.")])
self._errors['nationality_visa_specialist_visa'] = self.error_class([_("You must enter at least one Nationality & Visa Detail.")])
self._errors['nationality_visa_other_visa'] = self.error_class([_("You must enter at least one Nationality & Visa Detail.")])
self._errors['nationality_visa_current_valid_passport_display_type'] = self.error_class([_("You must enter at least one Nationality & Visa Detail.")])
self._errors['nationality_visa_passport_nationality'] = self.error_class([_("You must enter at least one Nationality & Visa Detail.")])
return cd_nvdf
希望这能对某些人有所帮助。