Stop using NullBooleanField
For four years the documentation for NullBooleanField
was two sentences and one of was “yeah…don’t use it”. As of 3.1 the axe has fallen and the hint of future deprecation has been replaced with an actual deprecation warning. Instead of using NullBooleanField()
use BooleanField(null=True)
.
On the face of it, the existence of NullBooleanField
seems odd. Why have an entire class that can be achieved with a null
keyword argument? We don’t see NullCharField
or NullDateField
. Indeed, for those Django expects us to doCharField(null=True)
and DateField(null=True)
. So what’s was so special about NullBooleanField
and why is it now deprecated?
Enter NullBooleanField
NullBooleanField
renders a NullBooleanSelect
widget which is a <select>
containing options “Unknown” (None
), “Yes” (True
) and “No” (False
). The implication is NullBooleanField
was intended for when explicitly stating no answer yet was needed. Indeed, in many contexts it would be useful to clarify “is it False
because the user has set it False
, or because the user has not yet answered?”. To facilitate that, the database column must allow NULL
(aka None
at Python level).
Unfortunately time has shown a great deal of room for confusion: StackOverflow has many questions that are answered with “use NullBooleanField instead of BooleanField” and vice versa. If one of the reasons for separating BooleanField
and NullBooleanField
was to give clarity then instead the opposite occurred for many.
Exit NullBooleanField
Until Django 2.1 in 2018, null
was not permitted in BooleanField
because quite obviously None
is not in a bool
value. Why would we expect None
to be used in a field that says it’s for boolean values? Well, on the other hand None
is not a str
either but CharField(null=True)
was supported and None
is not an int
, but IntegerField(null=True)
was also acceptable.
So in the deprecation of NullBooleanField
there was an argument for consistency with how the other fields handle null
. For consistency the choice was to either add NullCharField
, NullIntegerField
, NullDateField
and so on or to rename NullBooleanField
to BooleanField
. Even though NullBooleanField
was more explicit and a more of an accurate name.
With this deprecation three classes are impacted:
django.models.fields.NullBooleanField
django.forms.fields.BooleanField
django.forms.widgets.NullBooleanSelect
These three have slightly different handling of “empty” values, so for some the swap from NullBooleanField
to BooleanField
will need some careful testing:
from django.forms.fields import NullBooleanFieldfield = NullBooleanField()assert field.clean("True") is True
assert field.clean("") is None
assert field.clean(False) is False
from django.forms.fields import BooleanFieldfield = BooleanField(required=False)assert field.clean(True) is True
assert field.clean("") is False
assert field.clean(False) is Falsefrom django.db.models import fieldsfield = fields.BooleanField(null=True, blank=True)
assert field.clean(True, "test") is True
assert field.clean("", "test") is None
assert field.clean(False, "test") is False
I’m a GitHub bot that suggest Django improvements to your code. You can check your entire codebase at django.doctor or install the GitHub PR bot to reduce dev effort and improve your code.