Alex Rivera | Logout

How do I create a Django form that displays a checkbox label to the right of the checkbox?

Asked 2009-02-21T04:18:17.727
42

When I define a Django form class similar to this:

def class MyForm(forms.Form):
    check = forms.BooleanField(required=True, label="Check this")

It expands to HTML that looks like this:

<form action="." id="form" method=POST>
<p><label for="check">Check this:</label> <input type="checkbox" name="check" id="check" /></p>
<p><input type=submit value="Submit"></p>
</form>

I would like the checkbox input element to have a label that follows the checkbox, not the other way around. Is there a way to convince Django to do that?

[Edit]

Thanks for the answer from Jonas - still, while it fixes the issue I asked about (checkbox labels are rendered to the right of the checkbox) it introduces a new problem (all widget labels are rendered to the right of their widgets...)

I'd like to avoid overriding _html_output() since it's obviously not designed for it. The design I would come up with would be to implement a field html output method in the Field classes, override the one for the Boolean field and use that method in _html_output(). Sadly, the Django developers chose to go a different way, and I would like to work within the existing framework as much as possible.

CSS sounds like a decent approach, except that I don't know enough CSS to pull this off or even to decide whether I like this approach or not. Besides, I prefer markup that still resembles the final output, at least in rendering order.

Furthermore, since it can be reasonable to have more than one style sheet for any particular markup, doing this in CSS could mean having to do it multiple times for multiple styles, which pretty much makes CSS the wrong answer.

[Edit]

Seems like I'm answering my own question below. If anyone has a better idea how to do this, don't be shy.

Edit
Report

2 Answers

12

All presented solutions involve template modifications, which are in general rather inefficient concerning performance. Here's a custom widget that does the job:

from django import forms
from django.forms.fields import BooleanField
from django.forms.util import flatatt
from django.utils.encoding import force_text
from django.utils.html import format_html
from django.utils.translation import ugettext as _


class PrettyCheckboxWidget(forms.widgets.CheckboxInput):
    def render(self, name, value, attrs=None):
        final_attrs = self.build_attrs(attrs, type='checkbox', name=name)
        if self.check_test(value):
            final_attrs['checked'] = 'checked'
        if not (value is True or value is False or value is None or value == ''):
            final_attrs['value'] = force_text(value)
        if 'prettycheckbox-label' in final_attrs:
            label = _(final_attrs.pop('prettycheckbox-label'))
        else:
            label = ''
        return format_html('<label for="{0}"><input{1} /> {2}</label>', attrs['id'], flatatt(final_attrs), label)


class PrettyCheckboxField(BooleanField):
    widget = PrettyCheckboxWidget
    def __init__(self, *args, **kwargs):
        if kwargs['label']:
            kwargs['widget'].attrs['prettycheckbox-label'] = kwargs['label']
            kwargs['label'] = ''
        super(PrettyCheckboxField, self).__init__(*args, **kwargs)


# usage in form
class MyForm(forms.Form):
    my_boolean = PrettyCheckboxField(label=_('Some label'), widget=PrettyCheckboxWidget())

I have PrettyCheckboxWidget and PrettyCheckboxField in an extra file, so they may be imported where needed. If you don't need translations, you can remove the ugettext parts. This code works on Django 1.5 and is untested for lower versions.

Advantages:

  • Highly performant, needs no template modifications
  • Easy to use as a custom widget<
answered 2013-03-09T07:18:55.620
4

I know that the user excluded CSS, but considering the top answers take about half hour of work to do such a small thing, but knowing that details like these are important on a website, I'd settle for the CSS solution.

checkbox.css

input[type="checkbox"] {
    float: left;
    margin-right: 10px;
    margin-top: 4px;
}

forms.py

class MyForm(forms.ModelForm):
    # ...
    class Media:
    css = {
        'all': 'checkbox.css',
    }

template.html

{{ form.media }}
{{ form.as_p }}

Advantages:

  • fast!
  • no futzing with template tags (just form.as_p)
  • no new damned widgets
  • the CSS file is automatically included in every form

Disadvantages:

  • the HTML doesn't reflect the presentation (but is good enough!)
  • your frontendist could complain
answered 2013-07-23T11:27:15.230

Your Answer