Django自定义表单验证最佳实践?

Gru*_*uck 4 django validation django-forms

我有一个包含5对位置和描述的表单.我有三组验证需要完成

  • 您需要输入至少一个位置
  • 对于第一个位置,您必须有描述
  • 对于每对剩余的位置和描述

阅读完Django文档后,我想出了以下代码来进行这些自定义验证

def clean(self):
    cleaned_data = self.cleaned_data
    location1 = cleaned_data.get('location1')
    location2 = cleaned_data.get('location2')
    location3 = cleaned_data.get('location3')
    location4 = cleaned_data.get('location4')
    location5 = cleaned_data.get('location5')
    description1 = cleaned_data.get('description1')
    description2 = cleaned_data.get('description2')
    description3 = cleaned_data.get('description3')
    description4 = cleaned_data.get('description4')
    description5 = cleaned_data.get('description5')
    invalid_pairs_msg = u"You must specify a location and description"

    # We need to make sure that we have pairs of locations and descriptions
    if not location1:
        self._errors['location1'] = ErrorList([u"At least one location is required"])

    if location1 and not description1:
        self._errors['description1'] = ErrorList([u"Description for this location required"])

    if (description2 and not location2) or (location2 and not description2):
        self._errors['description2'] = ErrorList([invalid_pairs_msg])

    if (description3 and not location3) or (location3 and not description3):
        self._errors['description3'] = ErrorList([invalid_pairs_msg])

    if (description4 and not location4) or (location4 and not description4):
        self._errors['description4'] = ErrorList([invalid_pairs_msg])

    if (description5 and not location5) or (location5 and not description5):
        self._errors['description5'] = ErrorList([invalid_pairs_msg])

    return cleaned_data     
Run Code Online (Sandbox Code Playgroud)

现在,它有效,它看起来真的很难看.我正在寻找一种更"Pythonic"和"Djangoist"(?)方式来做到这一点.提前致谢.

TM.*_*TM. 5

您可以做的第一件事是简化您想要查看是否只填充两个字段中的一个的情况的测试.您可以通过xor以下方式实现逻辑:

if bool(description2) != bool(location2): 
Run Code Online (Sandbox Code Playgroud)

或者这样:

if bool(description2) ^ bool(location2):
Run Code Online (Sandbox Code Playgroud)

我还认为如果你分别为每个字段实现一个干净的方法会更清楚,正如文档中所解释的那样.这样可以确保错误显示在右侧字段上,并且您只需要引发错误forms.ValidationError而不是_errors直接访问该对象.

例如:

def _require_together(self, field1, field2):
    a = self.cleaned_data.get(field1)
    b = self.cleaned_data.get(field2)
    if bool(a) ^ bool(b):
        raise forms.ValidationError(u'You must specify a location and description')
    return a

# use clean_description1 rather than clean_location1 since
# we want the error to be on description1
def clean_description1(self):
    return _require_together('description1', 'location1')

def clean_description2(self):
    return _require_together('description2', 'location2')

def clean_description3(self):
    return _require_together('description3', 'location3')

def clean_description4(self):
    return _require_together('description4', 'location4')

def clean_description5(self):
    return _require_together('description5', 'location5')
Run Code Online (Sandbox Code Playgroud)

为了获得location1所需的行为,只需使用required=True该字段,它将自动处理.