2👍
Here’s what I ended up doing. I wrote a custom template stringfilter to switch the tags around. Now, my template code looks like this:
{% load pretty_forms %}
<form action="." method="POST">
{{ form.as_p|pretty_checkbox }}
<p><input type="submit" value="Submit"></p>
</form>
The only difference from a plain Django template is the addition of the {% load %} template tag and the pretty_checkbox filter.
Here’s a functional but ugly implementation of pretty_checkbox – this code doesn’t have any error handling, it assumes that the Django generated attributes are formatted in a very specific way, and it would be a bad idea to use anything like this in your code:
from django import template
from django.template.defaultfilters import stringfilter
import logging
register=template.Library()
@register.filter(name='pretty_checkbox')
@stringfilter
def pretty_checkbox(value):
# Iterate over the HTML fragment, extract <label> and <input> tags, and
# switch the order of the pairs where the input type is "checkbox".
scratch = value
output = ''
try:
while True:
ls = scratch.find('<label')
if ls > -1:
le = scratch.find('</label>')
ins = scratch.find('<input')
ine = scratch.find('/>', ins)
# Check whether we're dealing with a checkbox:
if scratch[ins:ine+2].find(' type="checkbox" ')>-1:
# Switch the tags
output += scratch[:ls]
output += scratch[ins:ine+2]
output += scratch[ls:le-1]+scratch[le:le+8]
else:
output += scratch[:ine+2]
scratch = scratch[ine+2:]
else:
output += scratch
break
except:
logging.error("pretty_checkbox caught an exception")
return output
pretty_checkbox scans its string argument, finds pairs of <label> and <input> tags, and switches them around if the <input> tag’s type is “checkbox”. It also strips the last character of the label, which happens to be the ‘:’ character.
Advantages:
- No futzing with CSS.
- The markup ends up looking the way it’s supposed to.
- I didn’t hack Django internals.
- The template is nice, compact and idiomatic.
Disadvantages:
- The filter code needs to be tested for exciting values of the labels and input field names.
- There’s probably something somewhere out there that does it better and faster.
- More work than I planned on doing on a Saturday.
33👍
Here’s a solution I’ve come up with (Django v1.1):
{% load myfilters %}
[...]
{% for field in form %}
[...]
{% if field.field.widget|is_checkbox %}
{{ field }}{{ field.label_tag }}
{% else %}
{{ field.label_tag }}{{ field }}
{% endif %}
[...]
{% endfor %}
You’ll need to create a custom template tag (in this example in a “myfilters.py” file) containing something like this:
from django import template
from django.forms.fields import CheckboxInput
register = template.Library()
@register.filter(name='is_checkbox')
def is_checkbox(value):
return isinstance(value, CheckboxInput)
More info on custom template tags available here.
Edit: in the spirit of asker’s own answer:
Advantages:
- No futzing with CSS.
- The markup ends up looking the way it’s supposed to.
- I didn’t hack Django internals. (but had to look at quite a bunch)
- The template is nice, compact and idiomatic.
- The filter code plays nice regardless of the exact values of the labels and input field names.
Disadvantages:
- There’s probably something somewhere out there that does it better and faster.
- Unlikely that the client will be willing to pay for all the time spent on this just to move the label to the right…
- [Django]-How do you insert a template into another template?
- [Django]-How can I set two primary key fields for my models in Django?
- [Django]-Django: relation "django_site" does not exist
15👍
I took the answer from romkyns and made it a little more general
def field_type(field, ftype):
try:
t = field.field.widget.__class__.__name__
return t.lower() == ftype
except:
pass
return False
This way you can check the widget type directly with a string
{% if field|field_type:'checkboxinput' %}
<label>{{ field }} {{ field.label }}</label>
{% else %}
<label> {{ field.label }} </label> {{ field }}
{% endif %}
- [Django]-AssertionError: `HyperlinkedIdentityField` requires the request in the serializer context
- [Django]-Django App Improperly Configured – The app module has multiple filesystem locations
- [Django]-What is the function of the name parameter in django.urls.path?
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
Disadvantages:
- “as_table” renders the checkbox + label inside the second column
- {{ field.label }} inside the template is empty. The label is instead bound to {{ field }}
- More work than I planned on doing on a Saturday 😉
- [Django]-How to repeat a "block" in a django template
- [Django]-How to get the name of current app within a template?
- [Django]-Download a remote image and save it to a Django model
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
- [Django]-How to pass django rest framework response to html?
- [Django]-Get virtualenv's bin folder path from script
- [Django]-How to read the database table name of a Model instance?
2👍
Changing checkbox position in Django admin can be quite tricky, but luckily there is a simple solution using custom widget:
from django.forms.widgets import Widget, CheckboxInput, boolean_check
class RightCheckbox(Widget):
render = CheckboxInput().render
def __init__(self, attrs=None, check_test=None):
super(RightCheckbox, self).__init__(attrs)
self.check_test = boolean_check if check_test is None else check_test
Django uses left position only when widget is subclass of CheckboxInput
- [Django]-Alternate Row Coloring in Django Template with More Than One Set of Rows
- [Django]-Django Queryset with year(date) = '2010'
- [Django]-Django – How to make a variable available to all templates?
1👍
Order of inputs and labels is provided via normal_row parameter of the form and there are no different row patterns for checkboxes. So there are two ways to do this (in 0.96 version exactly):
1. override _html_output of the form
2. use CSS to change position of the label and checkbox
- [Django]-Django Template Language: Using a for loop with else
- [Django]-Simple approach to launching background task in Django
- [Django]-How Can I Disable Authentication in Django REST Framework
0👍
I had this problem too and I just used this method :
post-detail.html
{% if field.html_name == "check_box" %}
{{ field }}
<label for="{{ field.id_for_label }}" class="form-label">
{{ field.label }}
</label>
{% else %}
<label for="{{ field.id_for_label }}" class="form-label">
{{ field.label }}
</label>
{{ field }}
{% endif %}
- [Django]-How to use pdb.set_trace() in a Django unittest?
- [Django]-How to override and extend basic Django admin templates?
- [Django]-UUID as default value in Django model