Thursday, May 24, 2012

Django 1.4 raises PicklingError for Forms with Fields of Type BooleanField

The latest version of Django as of writing this post is 1.4, which contains a regression regarding the picklability of forms that contain fields of type BooleanField. The code for the widget that renders the BooleanField as a checkbox contains a lambda, which is not picklable. There is a fix in the Django github repository, but you might want to use the latest release version for production code.

I recently ran into this issue when trying to store a form object in the session. Regardless of whether this is a good idea, if you find yourself staring at the error below and scratching your head, you might be interested in a quick and dirty workaround.


Traceback:

File "/Users/hs/code/venv_django_fail/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  188.                 response = middleware_method(request, response)
File "/Users/hs/code/venv_django_fail/lib/python2.7/site-packages/django/contrib/sessions/middleware.py" in process_response
  36.                 request.session.save()
File "/Users/hs/code/venv_django_fail/lib/python2.7/site-packages/django/contrib/sessions/backends/db.py" in save
  52.             session_data=self.encode(self._get_session(no_load=must_create)),
File "/Users/hs/code/venv_django_fail/lib/python2.7/site-packages/django/contrib/sessions/backends/base.py" in encode
  79.         pickled = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL)

Exception Type: PicklingError at /
Exception Value: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed

Patching the Django code in my virtualenv seemed like a terrible idea. So instead I took the fixed code from the current Django development version and put it into a widgets.py file in my app directory. Let's say you have this form:


class FunnyForm(forms.Form):

    name = forms.CharField(label=u"What's your name?")
    is_awesome = forms.BooleanField(label=u'Are you awesome?', initial=True)

All you need to do is put this widgets.py file into your app directory, add
from widgets import PicklableCheckboxInput
to the top of your views.py file and change the widget for the BooleanField in the form like this:


class FunnyForm(forms.Form):

    name = forms.CharField(label=u"What's your name?")
    is_awesome = forms.BooleanField(label=u'Are you awesome?', initial=True,
        widget=PicklableCheckboxInput)