diff --git a/django/contrib/flatpages/models.py b/django/contrib/flatpages/models.py index 896bfa3eac..42bb3adf23 100644 --- a/django/contrib/flatpages/models.py +++ b/django/contrib/flatpages/models.py @@ -11,10 +11,12 @@ class FlatPage(models.Model): url = models.CharField(_('URL'), max_length=100, db_index=True) title = models.CharField(_('title'), max_length=200) content = models.TextField(_('content'), blank=True) - enable_comments = models.BooleanField(_('enable comments')) + enable_comments = models.BooleanField(_('enable comments'), default=False) template_name = models.CharField(_('template name'), max_length=70, blank=True, help_text=_("Example: 'flatpages/contact_page.html'. If this isn't provided, the system will use 'flatpages/default.html'.")) - registration_required = models.BooleanField(_('registration required'), help_text=_("If this is checked, only logged-in users will be able to view the page.")) + registration_required = models.BooleanField(_('registration required'), + help_text=_("If this is checked, only logged-in users will be able to view the page."), + default=False) sites = models.ManyToManyField(Site) class Meta: diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 1f0ce5e4ed..142b33f6a7 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -620,8 +620,6 @@ class BooleanField(Field): def __init__(self, *args, **kwargs): kwargs['blank'] = True - if 'default' not in kwargs and not kwargs.get('null'): - kwargs['default'] = False Field.__init__(self, *args, **kwargs) def get_internal_type(self): diff --git a/docs/ref/models/fields.txt b/docs/ref/models/fields.txt index 421de74c62..f22436e5fe 100644 --- a/docs/ref/models/fields.txt +++ b/docs/ref/models/fields.txt @@ -377,6 +377,10 @@ The default form widget for this field is a If you need to accept :attr:`~Field.null` values then use :class:`NullBooleanField` instead. +.. versionchanged:: 1.6 + The default value of ``BooleanField`` was changed from ``False`` to + ``None`` when :attr:`Field.default` isn't defined. + ``CharField`` ------------- diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt index db9c597490..f3d12cac38 100644 --- a/docs/releases/1.6.txt +++ b/docs/releases/1.6.txt @@ -292,6 +292,20 @@ should either restore Django's defaults at the end of each request, force an appropriate value at the beginning of each request, or disable persistent connections. +``BooleanField`` no longer defaults to ``False`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When a :class:`~django.db.models.BooleanField` doesn't have an explicit +:attr:`~django.db.models.Field.default`, the implicit default value is +``None``. In previous version of Django, it was ``False``, but that didn't +represent accurantely the lack of a value. + +Code that relies on the default value being ``False`` may raise an exception +when saving new model instances to the database, because ``None`` isn't an +acceptable value for a :class:`~django.db.models.BooleanField`. You should +either specify ``default=False`` explicitly on the field definition, or ensure +the field is set to ``True`` or ``False`` before saving the object. + Translations and comments in templates ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py index eaf84773e3..d3b1e4b868 100644 --- a/tests/model_fields/tests.py +++ b/tests/model_fields/tests.py @@ -6,7 +6,7 @@ from decimal import Decimal from django import test from django import forms from django.core.exceptions import ValidationError -from django.db import models +from django.db import models, IntegrityError from django.db.models.fields.files import FieldFile from django.utils import six from django.utils import unittest @@ -265,6 +265,18 @@ class BooleanFieldTests(unittest.TestCase): self.assertEqual(mc.bf.bfield, False) self.assertEqual(mc.nbf.nbfield, False) + def test_null_default(self): + """ + Check that a BooleanField defaults to None -- which isn't + a valid value (#15124). + """ + b = BooleanModel() + self.assertIsNone(b.bfield) + with self.assertRaises(IntegrityError): + b.save() + nb = NullBooleanModel() + self.assertIsNone(nb.nbfield) + nb.save() # no error class ChoicesTests(test.TestCase): def test_choices_and_field_display(self): diff --git a/tests/model_inheritance_regress/tests.py b/tests/model_inheritance_regress/tests.py index 98216dbc84..c2c9337485 100644 --- a/tests/model_inheritance_regress/tests.py +++ b/tests/model_inheritance_regress/tests.py @@ -181,11 +181,11 @@ class ModelInheritanceTest(TestCase): """ Regression test for #6755 """ - r = Restaurant(serves_pizza=False) + r = Restaurant(serves_pizza=False, serves_hot_dogs=False) r.save() self.assertEqual(r.id, r.place_ptr_id) orig_id = r.id - r = Restaurant(place_ptr_id=orig_id, serves_pizza=True) + r = Restaurant(place_ptr_id=orig_id, serves_pizza=True, serves_hot_dogs=False) r.save() self.assertEqual(r.id, orig_id) self.assertEqual(r.id, r.place_ptr_id)