• Skip to main content

zoiaorg

Tech, Strategy, and other interesting things by Roberto Zoiaby Roberto Zoia

  • Articles
  • Book Reviews
  • Effective Goal Setting
  • What I’m Reading
  • Archive
  • About

Django and Custom CheckboxSelectMultipleField

2006-02-22 by Roberto Zoia

Note: as of Django version 0.95, this solution no longer works. But thanks to the newforms module, it is no longer necessary. Explanation of how it works can be found in this post

The problem: I have been using Django in a project for some weeks. The application needs to display a series of checkboxes on the webpage, but the HTML rendering of the formfields.CheckboxSelectMultipleField class does not offer the precise placement of each checkbox that is needed on the webpage.

formfields.CheckboxSelectMultipleField is a descendant of SelectMultipleField and does not provide the ability to iterate through each of the possible choices, as formfields.RadioSelectField derived objects do:

<!-- choices is an formfields.RadioSelectField object
      This is not possible with formfields.CheckboxSelectMultipleField derived objects.
-->
<div class="form-row">
<table><tr>
     {% for i in form.choices.field_list %}
           <td>;{{ i.field }}</td><td>{{i.name}}</td>
     {% endfor %}
     </tr>
</table>
</div>

The Solution: a custom CheckboxesMultipleSelectField based on the code from formfields.RadioSelectField.
CustomCheckboxSelectMultipleField is intended to be used in a custom Manipulator.

The code for the custom CheckboxSelectMultiple

from django.core import formfields
class CustomCheckboxSelectMultipleField(formfields.FormField):
    def __init__(self, field_name, choices=[], ul_class='', is_required=False, validator_list=[], member_name=None):
        self.field_name = field_name
        # choices is a list of (value, human-readable key) tuples because order matters
        self.choices, self.is_required = choices, is_required
        self.validator_list = [self.isValidChoice] + validator_list
        self.ul_class = ul_class
        if member_name != None:
            self.member_name = member_name
    def render(self, data):
        """
        Returns a special object that is iterable *and*
        has a default str() rendered output.
        This allows for flexible use in templates. You can just use the default
        rendering:
            {{ field_name }}
        ...which will output the radio buttons in an unordered list.
        Or, you can manually traverse each radio option for special layout:
            {% for option in field_name.field_list %}
                {{ option.field }} {{ option.label }}<br />
            {% endfor %}
        """
        class CustomCheckBoxSelectMultipleRenderer:
            def __init__(self, datalist, ul_class):
                self.datalist, self.ul_class = datalist, ul_class
            def __str__(self):
                "Default str() output for this radio field -- a <ul>"
                output = ['</ul><ul %s>' % (self.ul_class and ' class="%s"' % self.ul_class or '')]
                output.extend(['<li>%s %s</li>' % (d['field'], d['label']) for d in self.datalist])
                output.append('</ul>')
                return ''.join(output)
            def __iter__(self):
                for d in self.datalist:
                    yield d
            def __len__(self):
                return len(self.datalist)
        datalist = []
        str_data_list = map(str, data) # normalize to string
        for i, (value, display_name) in enumerate(self.choices):
            checked_html = ''
            if str(value) in str_data_list:
                checked_html = 'checked="checked"'
            datalist.append({
                'value': value, 'checked': checked_html,
                'name': display_name,
                'field': '<input type="checkbox" id="%s" name="%s" value="%s"%s/>' % \
                    (self.get_id() + '_' + str(i), self.field_name, value, checked_html),
                'label': '<label for="%s">%s</label>' % \
                    (self.get_id() + '_' + str(i), display_name),
            })
        return CustomCheckBoxSelectMultipleRenderer(datalist, self.ul_class)
    def isValidChoice(self, data, form):
        str_data = str(data)
        str_choices = [str(item[0]) for item in self.choices]
        if str_data not in str_choices:
            raise validators.ValidationError, _("Select a valid choice; '%(data)s' is not in %(choices)s.") % {'data':str_data, 'choices':str_choices}

Filed Under: Legacy Tagged With: django, Python

Did you like this article?

Join our FREE Newsletter and receive updates directly to your inbox.

We hate SPAM. We'll keep your email address safe.

  • What I’m Focused on Now
  • Español
  • Privacy Policy

Copyright © 2021 Roberto Zoia
zoia.org runs on WordPress using a customized version of the Parallax Pro Theme for the Genesis Framework.

This website uses cookies to improve your experience.Accept
Privacy & Cookies Policy

Privacy Overview

This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary
Always Enabled

Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.

Non-necessary

Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.

SAVE & ACCEPT