django - 必须至少填入一个表单字段

3 投票
2 回答
2158 浏览
提问于 2025-04-18 18:15

我正在学习使用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 个回答

0

这里有一些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原则,也就是“不要重复自己”。试着找出你在做的事情中有哪些共同的模式,并利用这些模式。如果你在三四个地方写了同样的字符串数组,那就是个问题。把它们保存到一个列表中,然后遍历这个列表就好了。这样代码会简洁很多。

2

这里是一个可行的解决方案:

    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

希望这能对某些人有所帮助。

撰写回答