[Django]-Django form with BooleanField always invalid unless checked

46👍

In Django forms, Boolean fields must be created with required=False.

When the checkbox isn’t checked, browsers do not send the field in the POST parameters of requests. Without specifying that the field is optional Django will treat it as a missing field when not in the POST parameters.

Imho it would be nice for Django to have this behavior by default for boolean form fields..

(this was already answered in comments by Yuji but it would be useful as an answer)

40👍

initial=True should render the widget with checked="checked" so I don’t see the problem there.. but the reason the form is invalid is because all fields are required by default unless you specify otherwise.

Field.required¶ By default, each Field class assumes the value is
required, so if you pass an empty value — either None or the empty
string (“”) — then clean() will raise a ValidationError exception:

If you’d like a checkbox or any other value to be optional, you need to pass in required=False into the form field constructor.

up_to_date = forms.BooleanField(initial=True, required=False) 
# no longer required..

17👍

According to official docs:

If you want to include a boolean in your form that can be either True or False (e.g. a checked or unchecked checkbox), you must remember to pass in required=False when creating the BooleanField.

👤user

1👍

Just a hunch, but this could be about the basics – lost a few hours until realising something similar was just about basic variable types.

In my case, after creating the FORM (very basic, see below)

class para_selection(forms.Form):
    first=forms.BooleanField(initial=False, required=False)
    second=forms.BooleanField(initial=False, required=False)
    third=forms.BooleanField(initial=False, required=False)
    fourth=forms.BooleanField(initial=False, required=False)

… I was using
if selection == ‘True’:
.. in my view. But since the form is using Boolean values, the correct code would be:
if selection is True:

The first line will never work, because “==” is not used with Boolean type. Hopefully this was not the case, but wanted to flag it here since I kept trying and searching all over the Internet, without paying attention to the type of variable I was using.

0👍

Make sure you are not overwriting the initial form the first time the page loads.

I originally wasn’t checking if there was actually anything in request.GET. The first time the page loads request.GET is None, and calling SomeForm(request.GET) would actually wipe out the initial values.

def some_view(request):

    form = SomeForm()

    if request.method == 'GET':
        form = SomeForm(request.GET)

Adding a check to make sure there is something in request.GET fixed this.

def some_view(request):

    form = SomeForm()

    if request.method == 'GET' and request.GET:
        form = SomeForm(request.GET)

0👍

If you do as suggested, your up_to_date field will likely always be False. And you will never know if that is because the user wants it that way, or because the user skipped over the field!

I have the same problem with a legally required OPT_IN question. Where on signup, I need to get the user to make a conscience decision to allow my system to send them unsolicited e-mails (or not). I’m working with Django-Allauth and a custom user database.

I’ll share my solution below in the hope you can adapt it to your situation – I adapted this from the DOCS.

forms.py:

# ExtraFieldsSignup as per https://stackoverflow.com/a/12308807
class SignupFormExtraFields(forms.Form):
  CHOICES = (('1', 'YES, keep in touch',), ('2', 'No',))
  first_name = forms.CharField(max_length=30, label='Voornaam')
  last_name = forms.CharField(max_length=30, label='Achternaam')
  opt_in = forms.ChoiceField(widget=forms.RadioSelect, choices=CHOICES)

  def signup(self, request, user):
    user.first_name = self.cleaned_data['first_name']
    user.last_name = self.cleaned_data['last_name']
    # Now we figure out what the user chose:
    if self.cleaned_data['opt_in'] == '1':
        user.opt_in = True
    else:
        user.opt_in = False
    user.save()

Leave a comment