【问题标题】:Django custom form validation best practices?Django 自定义表单验证最佳实践?
【发布时间】:2011-01-11 12:07:26
【问题描述】:

我有一个包含 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     

现在,它可以工作了但是它看起来真的很难看。我正在寻找一种更“Pythonic”和“Djangoist”(?)的方式来做到这一点。提前致谢。

【问题讨论】:

    标签: django validation django-forms


    【解决方案1】:

    您可以做的第一件事是简化测试,以查看是否只填充了两个字段中的一个。你可以这样实现逻辑xor

    if bool(description2) != bool(location2): 
    

    或者这样:

    if bool(description2) ^ bool(location2):
    

    我还认为,如果您分别为每个字段实现一个干净的方法,这会更清楚,如the docs 中所述。这可以确保错误将显示在正确的字段中,并且让您只需引发 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')
    

    为了获得需要location1 的行为,只需将required=True 用于该字段,它将被自动处理。

    【讨论】:

    • 我是 Django 的新手,您能否详细说明创建装饰器有什么帮助(指向正确方向的链接会有所帮助)
    • 实际上,装饰器更像是一个 python 的东西,而不是一个 django 的东西。但是,在深入研究之后,我真的不认为装饰器会比制作辅助方法更简单。我已经更新了我的答案来展示一个例子。
    【解决方案2】:

    至少可以减少一些代码。有 'location1' 和 'description1' Required=True (正如 TM 和 stefanw 指出的那样)。那么,

    def clean(self):
       n=5
       data = self.cleaned_data
       l_d = [(data.get('location'+i),data.get('description'+i)) for i in xrange(1,n+1)]
       invalid_pairs_msg = u"You must specify a location and description"
    
       for i in xrange(1,n+1):
          if (l_d[i][1] and not l_d[i][0]) or (l_d[i][0] and not l_d[i][1]):
             self._errors['description'+i] = ErrorList([invalid_pairs_msg])
       return data
    

    虽然还是很丑...

    【讨论】:

    • 使 location1 和 description1 required=True 并且你可以省略中间部分(不要忘记调整 xranges)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-17
    • 2016-02-28
    • 2021-05-24
    • 2018-02-22
    • 2015-10-20
    • 2011-10-05
    • 2012-09-20
    相关资源
    最近更新 更多