Как Django BooleanField работает с RadioSelect?

У меня есть модель с булевым значением

class MyModel(models.Model):
    ...
    current_measures = models.BooleanField(
        verbose_name=_("Is there any control and safety measures?"),
        choices=BOOLEAN_CHOICE,
    )
    ...

У меня есть форма, которая должна установить значение для этого поля, используя значения true или false.

class MyForm(forms.ModelForm):
    ...
    current_measures = forms.BooleanField(
        widget=WidgetRadioSelectCustomAttrs(
            RADIO_WIDGET_ATTRS, choices=Risk.BOOLEAN_CHOICE
        ),
        label=_("Is there any control and safety measures?"),
        label_suffix="",
        required=True,
    )
    ...

Все работает нормально, пока я не установлю required=True для поля формы и не отправлю значение "False" в метод валидации. В этом случае возникает ValidationError('Current measures is required')

Как я выяснил в процессе отладки, проблема заключается в методе validate в forms.BooleanField. Сначала он запускает метод to_python, который преобразует мое значение 'False' (тип string) в False (тип bool).

def to_python(self, value):
    """Return a Python boolean object."""
    # Explicitly check for the string 'False', which is what a hidden field
    # will submit for False. Also check for '0', since this is what
    # RadioSelect will provide. Because bool("True") == bool('1') == True,
    # we don't need to handle that explicitly.
    if isinstance(value, str) and value.lower() in ("false", "0"):
        value = False
    else:
        value = bool(value)
    return super().to_python(value)

Затем выполняется метод validate класса BooleanField.

def validate(self, value):
    if not value and self.required:
        raise ValidationError(self.error_messages["required"], code="required")

Как вы можете видеть, он проверяет not value (в моем случае это False -> а не False -> True) и self.required (в моем случае также True, так как он задан в параметрах поля). Таким образом, True и True вызывают ошибку ValidationErorr, хотя на самом деле это не ошибка. Может ли кто-нибудь объяснить мне, что я делаю не так? Или это ошибка в Django?

Может ли кто-нибудь объяснить мне, что я делаю не так? Или это ошибка в Django?

Нет, required означает, что empty_value=… [Django-doc] не должен передаваться. Для BooleanField, empty_value является False, поэтому required=True подразумевает, что вы не можете установить его на элемент, соответствующий False.

Решением может быть работа с ChoiceField [Django-doc], а затем сопоставление его с соответствующим boolean:

class MyForm(forms.ModelForm):
    # …
    current_measures = forms.ChoiceField(
        widget=WidgetRadioSelectCustomAttrs(
            RADIO_WIDGET_ATTRS,
            choices=[(str(int(k)), v) for k, v in Risk.BOOLEAN_CHOICE],
        ),
        label=_('Is there any control and safety measures?'),
        label_suffix='',
        required=True,
    )

    def clean_current_measures(self):
        data = self.cleaned_data['current_measures']
        return bool(int(data))
Вернуться на верх