views:

1134

answers:

2

I'm rather new to Django and I'm using Django 1.0. I have this:
forms.py:

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

views.py:

def order_something(request):
    form = MyForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        # do stuff...

The problem is that the form is not valid unless the checkbox is checked, so there doesn't seem to be a way to get a False value from the field. As far as I can understand from the docs, it should work. It works if I add a CharField to my form...

Am I misunderstanding something here or is this a bug? (Yes, I have googled but found nothing relevant)

Update: As suggested by @Dominic Rodger, I tried adding a hidden field
dummy = forms.CharField(initial='dummy', widget=forms.widgets.HiddenInput())
and that makes the form valid. This workaround enables me to move on right now, but it would still be interesting to know if I'm misunderstanding something...

+2  A: 

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.

Orange Box
Actually, the default for 'required' is True, see http://docs.djangoproject.com/en/dev/ref/forms/fields/#required.But your answer made me spot my bug, so +1 for that! I'll answer my own question to make it clear to future readers what the problem was...
Niklas
I never said the default for required was True or False :) I just said it doesn't make a different since its impossible to leave a checkbox 'blank' its either selected or not selected. So, there is always a value - being required or not wont make a difference.
Orange Box
A: 

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