Как 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))