[Fixed]-Django form with just a BooleanField

11👍

There was a bug in the code in my question. Thanks to @d0ugal for helping me spot it by including a slightly different example. The problem was here:

form = MyForm(request.POST or None) # <- PROBLEM HERE!!!!!!!!!!!!!!!!
if request.method == 'POST' and form.is_valid():
    # do stuff...

The bug was that I assumed that request.POST would evaluate to True if it was a post. But since browsers don’t post anything for a not-checked checkbox, and that was the only field, the POST data was an empty dictionary, which evaluates to False. This caused None to be used as initialization data, causing the form to be unbound and not valid.
@d0ugal’s example does the safe thing and tests request.method first.

👤Niklas

4👍

This also works for me on 1.1, 1.0.3 and 1.0 (I have these three Virtual environments setup). I only tested this in FireFox so if its a browser issue thats another matter but as far as I know they all handle POST data with checkboxes the same.

Here is the full code for the project so you can reproduce at your leisure and compare with yours to see the difference.

Setting up in Ubuntu

$ django-admin.py startproject testing
$ cd testing/
$ python manage.py startapp myfirst

Then in the myfirst app folder;

/myfirst/views.py

from django.shortcuts import render_to_response

from myfirst.forms import MyForm

def testing(request):
    if request.method == 'POST':
        form = MyForm(request.POST)
        if form.is_valid():
            result = "valid"
        else:
            result = "not valid"
    else:
        form = MyForm()
        result = "no post"

    return render_to_response('test.html', {'form':form, 'result':result,})

/myfirst/forms.py
from django import forms

class MyForm(forms.Form):
    extra_cheeze = forms.BooleanField(required=False,initial=False,label='Extra cheeze')

/myfirst/templates/test.html

<html>
<head>
</head>
<body>
    <form action="." method="POST">
        {{ form }}
        <input type="submit" value="test">
    </form>
    {{ result }}
</body>
</html>

/urls.py
from django.conf.urls.defaults import *

from myfirst.views import testing

urlpatterns = patterns('',
     (r'.*', testing),
)

Then just run the project $ python manage.py runserver and browse to http://localhost:8000/. You’ll actually find that required doesn’t do anything with the checkbox, since you can’t leave it blank – a ‘blank’ answer is effectively ‘no’. If you want to make sure a user selects an answer have a multiple choice where the user has to select yes or no. You could force them to answer with radio buttons too.

1👍

This is not an answer, it describes the Django behaviour only.

Django 3.0.7 on Python 3.8.4rc1, Debian 10/testing.

My scenario is html <form> with more django forms.Form rendered classes inside, one of them has BooleanField only. (There are other small <forms> in page like a search form).

class OrderInvoiceTypeForm(forms.Form):
    is_business = forms.BooleanField(label="business", initial=False)  # with or without required=False

I submit via submit button which IS NOT in OrderInvoiceTypeForm but IS in the html .

With required=False form is always valid and is_business key is in .cleaned_data.

Without required (or with =True):

checked:

form_with_boolean.is_valid()    # True
form_with_boolean.cleaned_data  # {'is_business': True}

unchecked:

form_with_boolean.is_valid()    # False
form_with_boolean.cleaned_data  # {}

In 2nd case in dev tools I can see that browser (Chrome) doesn’t send the is_business value POST variable. Adding of hidden field will not help.

👤mirek

Leave a comment