Просмотр проверочной документации и исходного кода классов полей формы выявляет проблему и предлагает решение.
Поле формы обрабатывает свой ввод в этомпорядок в его clean
методе:
- вызов
to_python
для преобразования необработанного значения в правильный тип - вызов
validate
для преобразованного значения для проверки условий типа required
- вызов
run_validators
для преобразованного значения для проверки нарушений любого зарегистрированного валидатора
Метод to_python
A BooleanField
превращает любой ввод в True
или False
.Он не может вернуть None
или любое другое значение:
class BooleanField(Field):
widget = CheckboxInput
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)
Это означает, что ни один валидатор не может отличить ситуацию, в которой поле было присвоено None
, как значение от другой ситуации, которая привела к тому, что полевернуть False
как проанализированное значение Python.Вы можете использовать required=True
или явный валидатор, чтобы принудительно установить, что значение должно быть True
, если вы этого хотите, но вы не можете сказать, по каким разным причинам это значение может быть False
отдельно.
Комудля этого вам нужен другой класс - тот, который либо обрабатывает None
, и все, что вы не хотите принимать как недопустимый ввод перед преобразованием его в Python, или тот, который преобразует такие значения в None
и затем может использоватьвалидатор для отклонения None
значений.
Для первого решения вы можете создать подкласс и переопределить to_python
.Класс NullBooleanField
предоставляет хороший пример, но мы адаптируем его для обработки нулевого ввода как недопустимого, вместо того, чтобы позволять ему возвращать None
:
class StrictBooleanField(Field):
def to_python(self, value):
"""
Explicitly check for the string 'True' and 'False', which is what a
hidden field will submit for True and False, for 'true' and 'false',
which are likely to be returned by JavaScript serializations of forms,
and for '1' and '0', which is what a RadioField will submit. Unlike
the Booleanfield, this field must check for True because it doesn't
use the bool() function.
"""
if value in (True, 'True', 'true', '1'):
return True
elif value in (False, 'False', 'false', '0'):
return False
else:
raise ValidationError(_('Invalid boolean value %(value)s'), params={'value': value},', code='invalid')
для второго варианта (привлекательный, потому что он не'не требует специального подкласса поля) - это принять конфликт между именем поля и намерением и использовать NullBooleanField
напрямую, регистрируя валидатор, который обнаружит и отклонит нулевое значение.Что-то вроде:
def validate_non_null(value):
if value is None:
raise ValidationError(_('Value must not be None', code='invalid')
class ItemForm(ModelForm):
accept = NullBooleanField(validators=[validate_non_null])
class Meta:
model = Item
fields = ['accept']
Здесь мы используем NullBooleanField
, чтобы понимать ввод как True
, False
или None
, а не просто True
или False
, а затем с помощью стандартной проверки для обработки None
как распознанного, но неверного ввода.