diff --git a/django/forms/fields.py b/django/forms/fields.py index 3ff2819571..affe3879b9 100644 --- a/django/forms/fields.py +++ b/django/forms/fields.py @@ -586,9 +586,10 @@ class BooleanField(Field): def clean(self, value): """Returns a Python boolean object.""" # Explicitly check for the string 'False', which is what a hidden field - # will submit for False. Because bool("True") == True, we don't need to - # handle that explicitly. - if value == 'False': + # 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 value in ('False', '0'): value = False else: value = bool(value) @@ -607,13 +608,13 @@ class NullBooleanField(BooleanField): def clean(self, value): """ Explicitly checks for the string 'True' and 'False', which is what a - hidden field will submit for True and False. Unlike the - Booleanfield we also need to check for True, because we are not using - the bool() function + hidden field will submit for True and False, and for '1' and '0', which + is what a RadioField will submit. Unlike the Booleanfield we need to + explicitly check for True, because we are not using the bool() function """ - if value in (True, 'True'): + if value in (True, 'True', '1'): return True - elif value in (False, 'False'): + elif value in (False, 'False', '0'): return False else: return None diff --git a/tests/regressiontests/forms/fields.py b/tests/regressiontests/forms/fields.py index f66f2e1bb5..9d9d7227b9 100644 --- a/tests/regressiontests/forms/fields.py +++ b/tests/regressiontests/forms/fields.py @@ -1077,6 +1077,10 @@ False True >>> f.clean(0) False +>>> f.clean('1') +True +>>> f.clean('0') +False >>> f.clean('Django rocks') True @@ -1201,7 +1205,10 @@ True >>> f.clean(False) False >>> f.clean(None) +>>> f.clean('0') +False >>> f.clean('1') +True >>> f.clean('2') >>> f.clean('3') >>> f.clean('hello') @@ -1220,6 +1227,21 @@ True >>> f.cleaned_data['hidden_nullbool2'] False +# Make sure we're compatible with MySQL, which uses 0 and 1 for its boolean +# values. (#9609) +>>> NULLBOOL_CHOICES = (('1', 'Yes'), ('0', 'No'), ('', 'Unknown')) +>>> class MySQLNullBooleanForm(Form): +... nullbool0 = NullBooleanField(widget=RadioSelect(choices=NULLBOOL_CHOICES)) +... nullbool1 = NullBooleanField(widget=RadioSelect(choices=NULLBOOL_CHOICES)) +... nullbool2 = NullBooleanField(widget=RadioSelect(choices=NULLBOOL_CHOICES)) +>>> f = MySQLNullBooleanForm({ 'nullbool0': '1', 'nullbool1': '0', 'nullbool2': '' }) +>>> f.full_clean() +>>> f.cleaned_data['nullbool0'] +True +>>> f.cleaned_data['nullbool1'] +False +>>> f.cleaned_data['nullbool2'] + # MultipleChoiceField ######################################################### >>> f = MultipleChoiceField(choices=[('1', 'One'), ('2', 'Two')])