diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py
index f57ec283fc..470271c376 100644
--- a/django/db/backends/mysql/base.py
+++ b/django/db/backends/mysql/base.py
@@ -119,7 +119,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'JSONField': 'json',
- 'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveBigIntegerField': 'bigint UNSIGNED',
'PositiveIntegerField': 'integer UNSIGNED',
diff --git a/django/db/backends/mysql/operations.py b/django/db/backends/mysql/operations.py
index 5d2a981226..e43c121b4d 100644
--- a/django/db/backends/mysql/operations.py
+++ b/django/db/backends/mysql/operations.py
@@ -291,7 +291,7 @@ class DatabaseOperations(BaseDatabaseOperations):
def get_db_converters(self, expression):
converters = super().get_db_converters(expression)
internal_type = expression.output_field.get_internal_type()
- if internal_type in ['BooleanField', 'NullBooleanField']:
+ if internal_type == 'BooleanField':
converters.append(self.convert_booleanfield_value)
elif internal_type == 'DateTimeField':
if settings.USE_TZ:
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index d1650e7927..966eb4b6f4 100644
--- a/django/db/backends/oracle/base.py
+++ b/django/db/backends/oracle/base.py
@@ -127,7 +127,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
'BigIntegerField': 'NUMBER(19)',
'IPAddressField': 'VARCHAR2(15)',
'GenericIPAddressField': 'VARCHAR2(39)',
- 'NullBooleanField': 'NUMBER(1)',
'OneToOneField': 'NUMBER(11)',
'PositiveBigIntegerField': 'NUMBER(19)',
'PositiveIntegerField': 'NUMBER(11)',
@@ -143,7 +142,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
data_type_check_constraints = {
'BooleanField': '%(qn_column)s IN (0,1)',
'JSONField': '%(qn_column)s IS JSON',
- 'NullBooleanField': '%(qn_column)s IN (0,1)',
'PositiveBigIntegerField': '%(qn_column)s >= 0',
'PositiveIntegerField': '%(qn_column)s >= 0',
'PositiveSmallIntegerField': '%(qn_column)s >= 0',
diff --git a/django/db/backends/oracle/operations.py b/django/db/backends/oracle/operations.py
index b829d2bd9b..964edc4549 100644
--- a/django/db/backends/oracle/operations.py
+++ b/django/db/backends/oracle/operations.py
@@ -182,7 +182,7 @@ END;
converters.append(self.convert_textfield_value)
elif internal_type == 'BinaryField':
converters.append(self.convert_binaryfield_value)
- elif internal_type in ['BooleanField', 'NullBooleanField']:
+ elif internal_type == 'BooleanField':
converters.append(self.convert_booleanfield_value)
elif internal_type == 'DateTimeField':
if settings.USE_TZ:
diff --git a/django/db/backends/oracle/utils.py b/django/db/backends/oracle/utils.py
index 5665079aa2..bbfd7f6a39 100644
--- a/django/db/backends/oracle/utils.py
+++ b/django/db/backends/oracle/utils.py
@@ -73,7 +73,6 @@ class BulkInsertMapper:
'DurationField': INTERVAL,
'FloatField': NUMBER,
'IntegerField': NUMBER,
- 'NullBooleanField': NUMBER,
'PositiveBigIntegerField': NUMBER,
'PositiveIntegerField': NUMBER,
'PositiveSmallIntegerField': NUMBER,
diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py
index c752b5dba4..9eac005dd1 100644
--- a/django/db/backends/postgresql/base.py
+++ b/django/db/backends/postgresql/base.py
@@ -87,7 +87,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
'IPAddressField': 'inet',
'GenericIPAddressField': 'inet',
'JSONField': 'jsonb',
- 'NullBooleanField': 'boolean',
'OneToOneField': 'integer',
'PositiveBigIntegerField': 'bigint',
'PositiveIntegerField': 'integer',
diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py
index 9ce3208960..f8e1def982 100644
--- a/django/db/backends/sqlite3/base.py
+++ b/django/db/backends/sqlite3/base.py
@@ -104,7 +104,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'JSONField': 'text',
- 'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveBigIntegerField': 'bigint unsigned',
'PositiveIntegerField': 'integer unsigned',
diff --git a/django/db/backends/sqlite3/operations.py b/django/db/backends/sqlite3/operations.py
index 71ef000c93..faf96a1b97 100644
--- a/django/db/backends/sqlite3/operations.py
+++ b/django/db/backends/sqlite3/operations.py
@@ -277,7 +277,7 @@ class DatabaseOperations(BaseDatabaseOperations):
converters.append(self.get_decimalfield_converter(expression))
elif internal_type == 'UUIDField':
converters.append(self.convert_uuidfield_value)
- elif internal_type in ('NullBooleanField', 'BooleanField'):
+ elif internal_type == 'BooleanField':
converters.append(self.convert_booleanfield_value)
return converters
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 5b8b3cab23..0f53d9c30b 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -1987,13 +1987,13 @@ 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 = {
+ system_check_removed_details = {
'msg': (
- 'NullBooleanField is deprecated. Support for it (except in '
- 'historical migrations) will be removed in Django 4.0.'
+ 'NullBooleanField is removed except for support in historical '
+ 'migrations.'
),
'hint': 'Use BooleanField(null=True) instead.',
- 'id': 'fields.W903',
+ 'id': 'fields.E903',
}
def __init__(self, *args, **kwargs):
diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt
index 48ffb8c816..e79a50b831 100644
--- a/docs/ref/checks.txt
+++ b/docs/ref/checks.txt
@@ -206,7 +206,10 @@ Model fields
* **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.
+ in historical migrations) will be removed in Django 4.0. *This check appeared
+ in Django 3.1 and 3.2*.
+* **fields.E903**: ``NullBooleanField`` is removed except for support in
+ historical migrations.
* **fields.W904**: ``django.contrib.postgres.fields.JSONField`` is deprecated.
Support for it (except in historical migrations) will be removed in Django
4.0.
diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt
index 3409d2d023..aedf115e08 100644
--- a/docs/ref/models/fields.txt
+++ b/docs/ref/models/fields.txt
@@ -1254,17 +1254,6 @@ To query ``JSONField`` in the database, see :ref:`querying-jsonfield`.
objects and arrays (represented in Python using :py:class:`dict` and
:py:class:`list`) are supported.
-``NullBooleanField``
---------------------
-
-.. class:: NullBooleanField(**options)
-
-Like :class:`BooleanField` with ``null=True``.
-
-.. deprecated:: 3.1
-
- ``NullBooleanField`` is deprecated in favor of ``BooleanField(null=True)``.
-
``PositiveBigIntegerField``
---------------------------
diff --git a/docs/releases/2.1.txt b/docs/releases/2.1.txt
index 21e9388680..bc03a67dce 100644
--- a/docs/releases/2.1.txt
+++ b/docs/releases/2.1.txt
@@ -189,8 +189,8 @@ Models
now support using field transforms.
* :class:`~django.db.models.BooleanField` can now be ``null=True``. This is
- encouraged instead of :class:`~django.db.models.NullBooleanField`, which will
- likely be deprecated in the future.
+ encouraged instead of ``NullBooleanField``, which will likely be deprecated
+ in the future.
* The new :meth:`.QuerySet.explain` method displays the database's execution
plan of a queryset's query.
diff --git a/docs/releases/4.0.txt b/docs/releases/4.0.txt
index d3bb700aac..4e89cf2230 100644
--- a/docs/releases/4.0.txt
+++ b/docs/releases/4.0.txt
@@ -305,3 +305,6 @@ to remove usage of these features.
* The ``list`` message for ``ModelMultipleChoiceField`` is removed.
* Support for passing raw column aliases to ``QuerySet.order_by()`` is removed.
+
+* The ``NullBooleanField`` model field is removed, except for support in
+ historical migrations.
diff --git a/docs/topics/forms/modelforms.txt b/docs/topics/forms/modelforms.txt
index 586b668da9..179762474a 100644
--- a/docs/topics/forms/modelforms.txt
+++ b/docs/topics/forms/modelforms.txt
@@ -106,8 +106,6 @@ Model field Form field
:class:`ManyToManyField` :class:`~django.forms.ModelMultipleChoiceField`
(see below)
-:class:`NullBooleanField` :class:`~django.forms.NullBooleanField`
-
:class:`PositiveBigIntegerField` :class:`~django.forms.IntegerField`
:class:`PositiveIntegerField` :class:`~django.forms.IntegerField`
diff --git a/tests/admin_filters/models.py b/tests/admin_filters/models.py
index f286e641de..90b9cab2ac 100644
--- a/tests/admin_filters/models.py
+++ b/tests/admin_filters/models.py
@@ -29,7 +29,6 @@ class Book(models.Model):
blank=True, null=True,
)
is_best_seller = models.BooleanField(default=0, null=True)
- is_best_seller2 = models.NullBooleanField(default=0)
date_registered = models.DateField(null=True)
availability = models.BooleanField(choices=(
(False, 'Paid'),
diff --git a/tests/admin_filters/tests.py b/tests/admin_filters/tests.py
index 2ad44b6c8c..17aa54a6f6 100644
--- a/tests/admin_filters/tests.py
+++ b/tests/admin_filters/tests.py
@@ -144,10 +144,6 @@ class BookAdmin(ModelAdmin):
ordering = ('-id',)
-class BookAdmin2(ModelAdmin):
- list_filter = ('year', 'author', 'contributors', 'is_best_seller2', 'date_registered', 'no')
-
-
class BookAdminWithTupleBooleanFilter(BookAdmin):
list_filter = (
'year',
@@ -289,22 +285,22 @@ class ListFiltersTests(TestCase):
cls.djangonaut_book = Book.objects.create(
title='Djangonaut: an art of living', year=2009,
author=cls.alfred, is_best_seller=True, date_registered=cls.today,
- is_best_seller2=True, availability=True,
+ availability=True,
)
cls.bio_book = Book.objects.create(
title='Django: a biography', year=1999, author=cls.alfred,
is_best_seller=False, no=207,
- is_best_seller2=False, availability=False,
+ availability=False,
)
cls.django_book = Book.objects.create(
title='The Django Book', year=None, author=cls.bob,
is_best_seller=None, date_registered=cls.today, no=103,
- is_best_seller2=None, availability=True,
+ availability=True,
)
cls.guitar_book = Book.objects.create(
title='Guitar for dummies', year=2002, is_best_seller=True,
date_registered=cls.one_week_ago,
- is_best_seller2=True, availability=None,
+ availability=None,
)
cls.guitar_book.contributors.set([cls.bob, cls.lisa])
@@ -1014,58 +1010,6 @@ class ListFiltersTests(TestCase):
self.assertIs(choice['selected'], True)
self.assertEqual(choice['query_string'], '?')
- def test_booleanfieldlistfilter_nullbooleanfield(self):
- modeladmin = BookAdmin2(Book, site)
-
- request = self.request_factory.get('/')
- request.user = self.alfred
- changelist = modeladmin.get_changelist_instance(request)
-
- request = self.request_factory.get('/', {'is_best_seller2__exact': 0})
- request.user = self.alfred
- changelist = modeladmin.get_changelist_instance(request)
-
- # Make sure the correct queryset is returned
- queryset = changelist.get_queryset(request)
- self.assertEqual(list(queryset), [self.bio_book])
-
- # Make sure the correct choice is selected
- filterspec = changelist.get_filters(request)[0][3]
- self.assertEqual(filterspec.title, 'is best seller2')
- choice = select_by(filterspec.choices(changelist), "display", "No")
- self.assertIs(choice['selected'], True)
- self.assertEqual(choice['query_string'], '?is_best_seller2__exact=0')
-
- request = self.request_factory.get('/', {'is_best_seller2__exact': 1})
- request.user = self.alfred
- changelist = modeladmin.get_changelist_instance(request)
-
- # Make sure the correct queryset is returned
- queryset = changelist.get_queryset(request)
- self.assertEqual(list(queryset), [self.guitar_book, self.djangonaut_book])
-
- # Make sure the correct choice is selected
- filterspec = changelist.get_filters(request)[0][3]
- self.assertEqual(filterspec.title, 'is best seller2')
- choice = select_by(filterspec.choices(changelist), "display", "Yes")
- self.assertIs(choice['selected'], True)
- self.assertEqual(choice['query_string'], '?is_best_seller2__exact=1')
-
- request = self.request_factory.get('/', {'is_best_seller2__isnull': 'True'})
- request.user = self.alfred
- changelist = modeladmin.get_changelist_instance(request)
-
- # Make sure the correct queryset is returned
- queryset = changelist.get_queryset(request)
- self.assertEqual(list(queryset), [self.django_book])
-
- # Make sure the correct choice is selected
- filterspec = changelist.get_filters(request)[0][3]
- self.assertEqual(filterspec.title, 'is best seller2')
- choice = select_by(filterspec.choices(changelist), "display", "Unknown")
- self.assertIs(choice['selected'], True)
- self.assertEqual(choice['query_string'], '?is_best_seller2__isnull=True')
-
def test_fieldlistfilter_underscorelookup_tuple(self):
"""
Ensure ('fieldpath', ClassName ) lookups pass lookup_allowed checks
diff --git a/tests/admin_utils/tests.py b/tests/admin_utils/tests.py
index a74449bdc0..5960759a4d 100644
--- a/tests/admin_utils/tests.py
+++ b/tests/admin_utils/tests.py
@@ -163,12 +163,6 @@ class UtilsTests(SimpleTestCase):
display_value = display_for_field(None, models.TimeField(), self.empty_value)
self.assertEqual(display_value, self.empty_value)
- # Regression test for #13071: NullBooleanField has special
- # handling.
- display_value = display_for_field(None, models.NullBooleanField(), self.empty_value)
- expected = '' % settings.STATIC_URL
- self.assertHTMLEqual(display_value, expected)
-
display_value = display_for_field(None, models.BooleanField(null=True), self.empty_value)
expected = '' % settings.STATIC_URL
self.assertHTMLEqual(display_value, expected)
diff --git a/tests/annotations/tests.py b/tests/annotations/tests.py
index 8337f344ed..2aa1765125 100644
--- a/tests/annotations/tests.py
+++ b/tests/annotations/tests.py
@@ -4,8 +4,8 @@ from decimal import Decimal
from django.core.exceptions import FieldDoesNotExist, FieldError
from django.db.models import (
BooleanField, Case, CharField, Count, DateTimeField, DecimalField, Exists,
- ExpressionWrapper, F, FloatField, Func, IntegerField, Max,
- NullBooleanField, OuterRef, Q, Subquery, Sum, Value, When,
+ ExpressionWrapper, F, FloatField, Func, IntegerField, Max, OuterRef, Q,
+ Subquery, Sum, Value, When,
)
from django.db.models.expressions import RawSQL
from django.db.models.functions import (
@@ -641,14 +641,12 @@ class NonAggregateAnnotationTestCase(TestCase):
is_book=Value(True, output_field=BooleanField()),
is_pony=Value(False, output_field=BooleanField()),
is_none=Value(None, output_field=BooleanField(null=True)),
- is_none_old=Value(None, output_field=NullBooleanField()),
)
self.assertGreater(len(books), 0)
for book in books:
self.assertIs(book.is_book, True)
self.assertIs(book.is_pony, False)
self.assertIsNone(book.is_none)
- self.assertIsNone(book.is_none_old)
def test_annotation_in_f_grouped_by_annotation(self):
qs = (
diff --git a/tests/backends/oracle/tests.py b/tests/backends/oracle/tests.py
index 258f98f5c9..85d45805e0 100644
--- a/tests/backends/oracle/tests.py
+++ b/tests/backends/oracle/tests.py
@@ -1,7 +1,7 @@
import unittest
from django.db import DatabaseError, connection
-from django.db.models import BooleanField, NullBooleanField
+from django.db.models import BooleanField
from django.test import TransactionTestCase
from ..models import Square
@@ -48,7 +48,7 @@ class Tests(unittest.TestCase):
def test_boolean_constraints(self):
"""Boolean fields have check constraints on their values."""
- for field in (BooleanField(), NullBooleanField(), BooleanField(null=True)):
+ for field in (BooleanField(), BooleanField(null=True)):
with self.subTest(field=field):
field.set_attributes_from_name('is_nice')
self.assertIn('"IS_NICE" IN (0,1)', field.db_check(connection))
diff --git a/tests/bulk_create/models.py b/tests/bulk_create/models.py
index 9bde8a3976..586457b192 100644
--- a/tests/bulk_create/models.py
+++ b/tests/bulk_create/models.py
@@ -83,7 +83,6 @@ class NullableFields(models.Model):
float_field = models.FloatField(null=True, default=3.2)
integer_field = models.IntegerField(null=True, default=2)
null_boolean_field = models.BooleanField(null=True, default=False)
- null_boolean_field_old = models.NullBooleanField(null=True, default=False)
positive_big_integer_field = models.PositiveBigIntegerField(null=True, default=2 ** 63 - 1)
positive_integer_field = models.PositiveIntegerField(null=True, default=3)
positive_small_integer_field = models.PositiveSmallIntegerField(null=True, default=4)
diff --git a/tests/datatypes/models.py b/tests/datatypes/models.py
index ce78470f61..b1304a7cc7 100644
--- a/tests/datatypes/models.py
+++ b/tests/datatypes/models.py
@@ -10,7 +10,6 @@ class Donut(models.Model):
name = models.CharField(max_length=100)
is_frosted = models.BooleanField(default=False)
has_sprinkles = models.BooleanField(null=True)
- has_sprinkles_old = models.NullBooleanField()
baked_date = models.DateField(null=True)
baked_time = models.TimeField(null=True)
consumed_at = models.DateTimeField(null=True)
diff --git a/tests/datatypes/tests.py b/tests/datatypes/tests.py
index 924d796121..52f24fe051 100644
--- a/tests/datatypes/tests.py
+++ b/tests/datatypes/tests.py
@@ -12,18 +12,14 @@ class DataTypesTestCase(TestCase):
d = Donut(name='Apple Fritter')
self.assertFalse(d.is_frosted)
self.assertIsNone(d.has_sprinkles)
- self.assertIsNone(d.has_sprinkles_old)
d.has_sprinkles = True
- d.has_sprinkles_old = True
self.assertTrue(d.has_sprinkles)
- self.assertTrue(d.has_sprinkles_old)
d.save()
d2 = Donut.objects.get(name='Apple Fritter')
self.assertFalse(d2.is_frosted)
self.assertTrue(d2.has_sprinkles)
- self.assertTrue(d2.has_sprinkles_old)
def test_date_type(self):
d = Donut(name='Apple Fritter')
diff --git a/tests/expressions_case/models.py b/tests/expressions_case/models.py
index 8e8e33a678..243e645005 100644
--- a/tests/expressions_case/models.py
+++ b/tests/expressions_case/models.py
@@ -26,7 +26,6 @@ class CaseTestModel(models.Model):
image = models.ImageField(null=True)
generic_ip_address = models.GenericIPAddressField(null=True)
null_boolean = models.BooleanField(null=True)
- null_boolean_old = models.NullBooleanField()
positive_integer = models.PositiveIntegerField(null=True)
positive_small_integer = models.PositiveSmallIntegerField(null=True)
positive_big_integer = models.PositiveSmallIntegerField(null=True)
diff --git a/tests/expressions_case/tests.py b/tests/expressions_case/tests.py
index ec811ca511..24443ab3a1 100644
--- a/tests/expressions_case/tests.py
+++ b/tests/expressions_case/tests.py
@@ -805,19 +805,6 @@ class CaseExpressionTests(TestCase):
transform=attrgetter('integer', 'null_boolean')
)
- def test_update_null_boolean_old(self):
- CaseTestModel.objects.update(
- null_boolean_old=Case(
- When(integer=1, then=True),
- When(integer=2, then=False),
- ),
- )
- self.assertQuerysetEqual(
- CaseTestModel.objects.all().order_by('pk'),
- [(1, True), (2, False), (3, None), (2, False), (3, None), (3, None), (4, None)],
- transform=attrgetter('integer', 'null_boolean_old')
- )
-
def test_update_positive_big_integer(self):
CaseTestModel.objects.update(
positive_big_integer=Case(
diff --git a/tests/field_deconstruction/tests.py b/tests/field_deconstruction/tests.py
index bf00aa44e2..b746e46458 100644
--- a/tests/field_deconstruction/tests.py
+++ b/tests/field_deconstruction/tests.py
@@ -432,13 +432,6 @@ class FieldDeconstructionTests(SimpleTestCase):
self.assertEqual(kwargs, {"to": "auth.Permission"})
self.assertEqual(kwargs['to'].setting_name, "AUTH_USER_MODEL")
- def test_null_boolean_field(self):
- field = models.NullBooleanField()
- name, path, args, kwargs = field.deconstruct()
- self.assertEqual(path, "django.db.models.NullBooleanField")
- self.assertEqual(args, [])
- self.assertEqual(kwargs, {})
-
def test_positive_integer_field(self):
field = models.PositiveIntegerField()
name, path, args, kwargs = field.deconstruct()
diff --git a/tests/invalid_models_tests/test_deprecated_fields.py b/tests/invalid_models_tests/test_deprecated_fields.py
index fdd5af1937..e240f20ba5 100644
--- a/tests/invalid_models_tests/test_deprecated_fields.py
+++ b/tests/invalid_models_tests/test_deprecated_fields.py
@@ -44,11 +44,11 @@ class DeprecatedFieldsTests(SimpleTestCase):
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.',
+ checks.Error(
+ 'NullBooleanField is removed except for support in historical '
+ 'migrations.',
hint='Use BooleanField(null=True) instead.',
obj=NullBooleanFieldModel._meta.get_field('nb'),
- id='fields.W903',
+ id='fields.E903',
),
])
diff --git a/tests/model_fields/models.py b/tests/model_fields/models.py
index c8867834da..9e8baeb565 100644
--- a/tests/model_fields/models.py
+++ b/tests/model_fields/models.py
@@ -135,7 +135,6 @@ class Post(models.Model):
class NullBooleanModel(models.Model):
nbfield = models.BooleanField(null=True, blank=True)
- nbfield_old = models.NullBooleanField()
class BooleanModel(models.Model):
@@ -192,16 +191,15 @@ class VerboseNameField(models.Model):
# field_image = models.ImageField("verbose field")
field11 = models.IntegerField("verbose field11")
field12 = models.GenericIPAddressField("verbose field12", protocol="ipv4")
- field13 = models.NullBooleanField("verbose field13")
- field14 = models.PositiveIntegerField("verbose field14")
- field15 = models.PositiveSmallIntegerField("verbose field15")
- field16 = models.SlugField("verbose field16")
- field17 = models.SmallIntegerField("verbose field17")
- field18 = models.TextField("verbose field18")
- field19 = models.TimeField("verbose field19")
- field20 = models.URLField("verbose field20")
- field21 = models.UUIDField("verbose field21")
- field22 = models.DurationField("verbose field22")
+ field13 = models.PositiveIntegerField("verbose field13")
+ field14 = models.PositiveSmallIntegerField("verbose field14")
+ field15 = models.SlugField("verbose field15")
+ field16 = models.SmallIntegerField("verbose field16")
+ field17 = models.TextField("verbose field17")
+ field18 = models.TimeField("verbose field18")
+ field19 = models.URLField("verbose field19")
+ field20 = models.UUIDField("verbose field20")
+ field21 = models.DurationField("verbose field21")
class GenericIPAddress(models.Model):
@@ -385,7 +383,6 @@ class AllFieldsModel(models.Model):
floatf = models.FloatField()
integer = models.IntegerField()
generic_ip = models.GenericIPAddressField()
- null_boolean = models.NullBooleanField()
positive_integer = models.PositiveIntegerField()
positive_small_integer = models.PositiveSmallIntegerField()
slug = models.SlugField()
diff --git a/tests/model_fields/test_booleanfield.py b/tests/model_fields/test_booleanfield.py
index 89e0ecfa2a..907385534b 100644
--- a/tests/model_fields/test_booleanfield.py
+++ b/tests/model_fields/test_booleanfield.py
@@ -26,18 +26,12 @@ class BooleanFieldTests(TestCase):
def test_nullbooleanfield_get_prep_value(self):
self._test_get_prep_value(models.BooleanField(null=True))
- def test_nullbooleanfield_old_get_prep_value(self):
- self._test_get_prep_value(models.NullBooleanField())
-
def test_booleanfield_to_python(self):
self._test_to_python(models.BooleanField())
def test_nullbooleanfield_to_python(self):
self._test_to_python(models.BooleanField(null=True))
- def test_nullbooleanfield_old_to_python(self):
- self._test_to_python(models.NullBooleanField())
-
def test_booleanfield_choices_blank(self):
"""
BooleanField with choices and defaults doesn't generate a formfield
@@ -59,8 +53,6 @@ class BooleanFieldTests(TestCase):
def test_nullbooleanfield_formfield(self):
f = models.BooleanField(null=True)
self.assertIsInstance(f.formfield(), forms.NullBooleanField)
- f = models.NullBooleanField()
- self.assertIsInstance(f.formfield(), forms.NullBooleanField)
def test_return_type(self):
b = BooleanModel.objects.create(bfield=True)
@@ -71,15 +63,13 @@ class BooleanFieldTests(TestCase):
b2.refresh_from_db()
self.assertIs(b2.bfield, False)
- b3 = NullBooleanModel.objects.create(nbfield=True, nbfield_old=True)
+ b3 = NullBooleanModel.objects.create(nbfield=True)
b3.refresh_from_db()
self.assertIs(b3.nbfield, True)
- self.assertIs(b3.nbfield_old, True)
- b4 = NullBooleanModel.objects.create(nbfield=False, nbfield_old=False)
+ b4 = NullBooleanModel.objects.create(nbfield=False)
b4.refresh_from_db()
self.assertIs(b4.nbfield, False)
- self.assertIs(b4.nbfield_old, False)
# When an extra clause exists, the boolean conversions are applied with
# an offset (#13293).
@@ -92,8 +82,8 @@ class BooleanFieldTests(TestCase):
"""
bmt = BooleanModel.objects.create(bfield=True)
bmf = BooleanModel.objects.create(bfield=False)
- nbmt = NullBooleanModel.objects.create(nbfield=True, nbfield_old=True)
- nbmf = NullBooleanModel.objects.create(nbfield=False, nbfield_old=False)
+ nbmt = NullBooleanModel.objects.create(nbfield=True)
+ nbmf = NullBooleanModel.objects.create(nbfield=False)
m1 = FksToBooleans.objects.create(bf=bmt, nbf=nbmt)
m2 = FksToBooleans.objects.create(bf=bmf, nbf=nbmf)
@@ -107,10 +97,8 @@ class BooleanFieldTests(TestCase):
mc = FksToBooleans.objects.select_related().get(pk=m2.id)
self.assertIs(mb.bf.bfield, True)
self.assertIs(mb.nbf.nbfield, True)
- self.assertIs(mb.nbf.nbfield_old, True)
self.assertIs(mc.bf.bfield, False)
self.assertIs(mc.nbf.nbfield, False)
- self.assertIs(mc.nbf.nbfield_old, False)
def test_null_default(self):
"""
@@ -126,7 +114,6 @@ class BooleanFieldTests(TestCase):
nb = NullBooleanModel()
self.assertIsNone(nb.nbfield)
- self.assertIsNone(nb.nbfield_old)
nb.save() # no error
@@ -142,5 +129,5 @@ class ValidationTest(SimpleTestCase):
NullBooleanField shouldn't throw a validation error when given a value
of None.
"""
- nullboolean = NullBooleanModel(nbfield=None, nbfield_old=None)
+ nullboolean = NullBooleanModel(nbfield=None)
nullboolean.full_clean()
diff --git a/tests/model_fields/test_promises.py b/tests/model_fields/test_promises.py
index 8e7f54b194..f48a4cc34a 100644
--- a/tests/model_fields/test_promises.py
+++ b/tests/model_fields/test_promises.py
@@ -5,9 +5,8 @@ from django.db.models import (
AutoField, BinaryField, BooleanField, CharField, DateField, DateTimeField,
DecimalField, EmailField, FileField, FilePathField, FloatField,
GenericIPAddressField, ImageField, IntegerField, IPAddressField,
- NullBooleanField, PositiveBigIntegerField, PositiveIntegerField,
- PositiveSmallIntegerField, SlugField, SmallIntegerField, TextField,
- TimeField, URLField,
+ PositiveBigIntegerField, PositiveIntegerField, PositiveSmallIntegerField,
+ SlugField, SmallIntegerField, TextField, TimeField, URLField,
)
from django.test import SimpleTestCase
from django.utils.functional import lazy
@@ -85,10 +84,6 @@ class PromiseTest(SimpleTestCase):
lazy_func = lazy(lambda: 0, int)
self.assertIsInstance(GenericIPAddressField().get_prep_value(lazy_func()), str)
- def test_NullBooleanField(self):
- lazy_func = lazy(lambda: True, bool)
- self.assertIsInstance(NullBooleanField().get_prep_value(lazy_func()), bool)
-
def test_PositiveIntegerField(self):
lazy_func = lazy(lambda: 1, int)
self.assertIsInstance(PositiveIntegerField().get_prep_value(lazy_func()), int)
diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py
index af2634dd63..5208b40dc9 100644
--- a/tests/model_fields/tests.py
+++ b/tests/model_fields/tests.py
@@ -56,7 +56,7 @@ class BasicFieldTests(SimpleTestCase):
def test_field_verbose_name(self):
m = VerboseNameField
- for i in range(1, 23):
+ for i in range(1, 22):
self.assertEqual(m._meta.get_field('field%d' % i).verbose_name, 'verbose field%d' % i)
self.assertEqual(m._meta.get_field('id').verbose_name, 'verbose pk')
diff --git a/tests/runtests.py b/tests/runtests.py
index ba7c163bf6..fe5ca44ba6 100755
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -191,7 +191,6 @@ 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.
diff --git a/tests/serializers/models/data.py b/tests/serializers/models/data.py
index eaa2aa60e6..94c4a215fc 100644
--- a/tests/serializers/models/data.py
+++ b/tests/serializers/models/data.py
@@ -70,10 +70,6 @@ class GenericIPAddressData(models.Model):
data = models.GenericIPAddressField(null=True)
-class NullBooleanData(models.Model):
- data = models.NullBooleanField(null=True)
-
-
class PositiveBigIntegerData(models.Model):
data = models.PositiveBigIntegerField(null=True)
diff --git a/tests/serializers/test_data.py b/tests/serializers/test_data.py
index 9fbdd256fe..91cad48e0f 100644
--- a/tests/serializers/test_data.py
+++ b/tests/serializers/test_data.py
@@ -23,8 +23,8 @@ from .models import (
GenericData, GenericIPAddressData, GenericIPAddressPKData,
InheritAbstractModel, InheritBaseModel, IntegerData, IntegerPKData,
Intermediate, LengthModel, M2MData, M2MIntermediateData, M2MSelfData,
- ModifyingSaveData, NullBooleanData, O2OData, PositiveBigIntegerData,
- PositiveIntegerData, PositiveIntegerPKData, PositiveSmallIntegerData,
+ ModifyingSaveData, O2OData, PositiveBigIntegerData, PositiveIntegerData,
+ PositiveIntegerPKData, PositiveSmallIntegerData,
PositiveSmallIntegerPKData, SlugData, SlugPKData, SmallData, SmallPKData,
Tag, TextData, TimeData, UniqueAnchor, UUIDData, UUIDDefaultData,
)
@@ -238,9 +238,6 @@ test_data = [
# (XX, ImageData
(data_obj, 95, GenericIPAddressData, "fe80:1424:2223:6cff:fe8a:2e8a:2151:abcd"),
(data_obj, 96, GenericIPAddressData, None),
- (data_obj, 100, NullBooleanData, True),
- (data_obj, 101, NullBooleanData, False),
- (data_obj, 102, NullBooleanData, None),
(data_obj, 110, PositiveBigIntegerData, 9223372036854775807),
(data_obj, 111, PositiveBigIntegerData, None),
(data_obj, 120, PositiveIntegerData, 123456789),
diff --git a/tests/validation/test_error_messages.py b/tests/validation/test_error_messages.py
index b8e4617886..5f1e0a75d0 100644
--- a/tests/validation/test_error_messages.py
+++ b/tests/validation/test_error_messages.py
@@ -36,8 +36,8 @@ class ValidationMessagesTest(TestCase):
self._test_validation_messages(f, 'fõo', ['“fõo” value must be a decimal number.'])
def test_null_boolean_field_raises_error_message(self):
- f = models.NullBooleanField()
- self._test_validation_messages(f, 'fõo', ['“fõo” value must be either None, True or False.'])
+ f = models.BooleanField(null=True)
+ self._test_validation_messages(f, 'fõo', ['“fõo” value must be either True, False, or None.'])
def test_date_field_raises_error_message(self):
f = models.DateField()