Форма модели Django удаляет обязательный атрибут на основе выбора другого поля

У меня есть ModelForm с несколькими полями. Некоторые поля обязательны для заполнения, некоторые нет. Также у меня есть поле «Выбрать» с разными вариантами, и я хочу сделать некоторые поля «обязательными» или нет на основе этого выбора поля «Выбрать».

Я пробовал в методе clean() формы

def clean(self):
    cleaned_data = self.cleaned_data
    some_field = cleaned_data.get("some_field")
    if some_field == 'some_value':
          self.fields['other_field'].required = False
    return cleaned_data

но это не работает


person Igor    schedule 17.08.2011    source источник


Ответы (3)


См. документацию Django по адресу Очистка и проверка полей, которые зависят друг от друга. Вместо этого стандартной практикой будет выполнять следующую обработку:

def clean(self):
    cleaned_data = self.cleaned_data
    some_field = cleaned_data.get("some_field")
    if some_field == 'some_value':
          # 'other_field' is conditionally required.
          if not cleaned_data['other_field']:
              raise forms.ValidationError("'Other_field' is required.")
    return cleaned_data
person André Caron    schedule 17.08.2011
comment
Спасибо. Эта идея помогла мне получить его! - person Igor; 17.08.2011
comment
Мне нужно сделать аналогичную проверку (Django 1.4), но поле, которое я тестирую, доступно только для чтения, поэтому его нет в моих clean_data (кстати, я делаю это в админке). Любая идея, как получить значение этого поля только для чтения? 10x - person Lin; 10.02.2013
comment
Решение найдено - form.instance содержит все соответствующие данные, а измененные данные включены в form.changed_data - person Lin; 11.02.2013
comment
Хотя это звучит как правильный подход, я думаю, что логика проверки some_field == some_value может быть продублирована в представлении. Не уверен, что это правильный путь? Жить с избыточностью или переместить эту проверку для просмотра? - person user; 04.07.2014
comment
@buffer: я не понимаю, почему представление должно повторять логику проверки, поскольку форма уже делает это. Если представление имеет аналогичную проверку (чтобы определить, использовать ли other_field или нет), этого нельзя избежать в двухпроходной системе (1 проход для проверки, 1 проход для потребления) AFAICT. При желании вы можете переместить эту логику только в представление, но затем вы можете повторить ту же логику для нескольких представлений, использующих одну и ту же форму. - person André Caron; 07.07.2014

Вы правы, но проблема в том, что проверка отдельных полей уже выполнена до того, как форма будет очищена. У вас есть пара вариантов. Вы можете сделать поле необязательным и обрабатывать логику, когда оно требуется в вашем form.clean. Или вы можете оставить поле по мере необходимости и удалить ошибки проверки, которые могут возникнуть при очистке.

def clean(self):
    cleaned_data = self.cleaned_data
    some_field = cleaned_data.get("some_field")
    if some_field == 'some_value':
          if 'other_field' in self.errors:
              del self.errors['other_field']
              cleaned_data['other_field'] = None
    return cleaned_data

У этого есть некоторые проблемы, поскольку он удаляет все ошибки, а не только отсутствующие/обязательные ошибки. Также есть проблема с cleaned_data. Теперь у вас есть обязательное поле, которого нет в clean_data, поэтому я добавил его как None. Остальная часть вашего приложения должна будет обрабатывать этот случай. Может показаться странным, что обязательное поле не имеет значения.

person Mark Lavin    schedule 17.08.2011

Если вы хотите распечатать сообщение об ошибке для обязательного поля обычным способом, вы можете сделать это:

def clean(self):
    cleaned_data = super(PasswordChangeForm, self).clean()
    token = cleaned_data.get('token')
    old_password = cleaned_data.get('old_password')
    if not token and not old_password:
        self._errors['old_password'] = self.error_class([self.fields['old_password'].error_messages['required']])
person Marboni    schedule 19.07.2012
comment
Этот ответ действительно очень полезен! Большое спасибо ! - person Serafeim; 16.05.2014