Лучшие практики проверки пользовательских форм в Django? - PullRequest
4 голосов
/ 31 января 2010

У меня есть форма, которая содержит 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" (?) Способ сделать это. Заранее спасибо.

Ответы [ 2 ]

5 голосов
/ 01 февраля 2010

Первое, что вы можете сделать, это упростить тестирование для тех случаев, когда вы хотите увидеть, заполнено ли только одно из двух полей. Вы можете реализовать логический xor следующим образом:

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

или так:

if bool(description2) ^ bool(location2):

Я также думаю, что это было бы более понятно, если бы вы внедрили метод очистки для каждого поля отдельно, как объяснено в документах . Это гарантирует, что ошибка будет отображаться в правом поле и позволит вам просто поднять 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 для этого поля, и оно будет обработано автоматически.

0 голосов
/ 01 февраля 2010

По крайней мере, вы можете уменьшить код. Иметь «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

все еще безобразно ...

...