diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index caa03665ea..d00942a7b8 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -1929,6 +1929,14 @@ class NullBooleanField(BooleanField): 'invalid_nullable': _('ā€œ%(value)sā€ value must be either None, True or False.'), } description = _("Boolean (Either True, False or None)") + system_check_deprecated_details = { + 'msg': ( + 'NullBooleanField is deprecated. Support for it (except in ' + 'historical migrations) will be removed in Django 4.0.' + ), + 'hint': 'Use BooleanField(null=True) instead.', + 'id': 'fields.W903', + } def __init__(self, *args, **kwargs): kwargs['null'] = True diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 76b379ef5a..8d3cc62d90 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -75,6 +75,9 @@ details on these changes. * Support for passing raw column aliases to ``QuerySet.order_by()`` will be removed. +* The model ``NullBooleanField`` will be removed. A stub field will remain for + compatibility with historical migrations. + See the :ref:`Django 3.1 release notes ` for more details on these changes. diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index d9bfdb168c..d9b56c3c58 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -202,6 +202,8 @@ Model fields in historical migrations. * **fields.W902**: ``FloatRangeField`` is deprecated and will be removed in Django 3.1. *This check appeared in Django 2.2 and 3.0*. +* **fields.W903**: ``NullBooleanField`` is deprecated. Support for it (except + in historical migrations) will be removed in Django 4.0. File fields ~~~~~~~~~~~ diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt index cbc3b6142c..19750eedc3 100644 --- a/docs/ref/models/fields.txt +++ b/docs/ref/models/fields.txt @@ -1182,8 +1182,11 @@ values are stored as null. .. class:: NullBooleanField(**options) -Like :class:`BooleanField` with ``null=True``. Use that instead of this field -as it's likely to be deprecated in a future version of Django. +Like :class:`BooleanField` with ``null=True``. + +.. deprecated:: 3.1 + + ``NullBooleanField`` is deprecated in favor of ``BooleanField(null=True)``. ``PositiveBigIntegerField`` --------------------------- diff --git a/docs/releases/3.1.txt b/docs/releases/3.1.txt index 029ad3150c..fbefef589f 100644 --- a/docs/releases/3.1.txt +++ b/docs/releases/3.1.txt @@ -733,6 +733,9 @@ Miscellaneous same result can be achieved by passing aliases in a :class:`~django.db.models.expressions.RawSQL` instead beforehand. +* The ``NullBooleanField`` model field is deprecated in favor of + ``BooleanField(null=True)``. + .. _removed-features-3.1: Features removed in 3.1 diff --git a/tests/invalid_models_tests/test_deprecated_fields.py b/tests/invalid_models_tests/test_deprecated_fields.py index ec713d95ff..fdd5af1937 100644 --- a/tests/invalid_models_tests/test_deprecated_fields.py +++ b/tests/invalid_models_tests/test_deprecated_fields.py @@ -37,3 +37,18 @@ class DeprecatedFieldsTests(SimpleTestCase): id='fields.E901', )], ) + + def test_nullbooleanfield_deprecated(self): + class NullBooleanFieldModel(models.Model): + nb = models.NullBooleanField() + + model = NullBooleanFieldModel() + self.assertEqual(model.check(), [ + checks.Warning( + 'NullBooleanField is deprecated. Support for it (except in ' + 'historical migrations) will be removed in Django 4.0.', + hint='Use BooleanField(null=True) instead.', + obj=NullBooleanFieldModel._meta.get_field('nb'), + id='fields.W903', + ), + ]) diff --git a/tests/runtests.py b/tests/runtests.py index a05779010c..8264d40684 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -181,6 +181,7 @@ def setup(verbosity, test_labels, parallel, start_at, start_after): settings.LOGGING = log_config settings.SILENCED_SYSTEM_CHECKS = [ 'fields.W342', # ForeignKey(unique=True) -> OneToOneField + 'fields.W903', # NullBooleanField deprecated. ] # Load all the ALWAYS_INSTALLED_APPS.