How to set css class of a label in a django form declaration?
I'm using django-uniform and to use some uniform features, I'm looking for a way to add css class directly from form declaration (for independents widgets).
(as a bonus, here my reusable read-only home made mixin snippet...)
from django import forms
def _get_cleaner(form, field):
def clean_field():
return getattr(form.instance, field, None)
return clean_field
class UniformROMixin(forms.BaseForm):
"""
UniformROMixin, inherits to turn some fields read only
- read_only = list of field names.
"""
def __init__(self, *args, **kwargs):
super(UniformROMixin, self).__init__(*args, **kwargs)
if hasattr(self, "read_only"):
if self.instance and self.instance.pk:
for field in self.read_only:
self.fields[field].widget.attrs['readonly'] = True
self.fields[field].widget.attrs['class'] += "readOnly"
# here I would like to set css class of the label
# created from the self.fields[field].label string
setattr(self, "clean_" + field, _get_cleaner(self, field))
My only way for now is to add a bit of javascript on my generic form template and add classes manualy.
Any brillant idea?
Solution 1:
Widgets have an attrs
keyword argument that take a dict
which can define attributes for the input element that it renders. Forms also have some attributes you can define to change how Django displays your form. Take the following example:
class MyForm(forms.Form):
error_css_class = 'error'
required_css_class = 'required'
my_field = forms.CharField(max_length=10,
widget=forms.TextInput(attrs={'id': 'my_field',
'class': 'my_class'}))
This works on any Widget
class. Unfortunately, there isn't an easy way to change how Django renders labels if you just do {{ field }}
. Luckily, you play with the form object a little bit in the template:
<form>
{% for field in form %}
<label class="my_class" for="{{ field.auto_id }}">{{ field.label }}</label>
{{ field }}
{% endfor %}
<button type="submit">Submit</button>
</form>
Then again, you can always add arbitrary attributes to the objects you're working with:
# In a view...
form = MyForm()
form.label_classes = ('class_a', 'class_b', )
# Or by hijacking ```__init__```
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
self.my_field = forms.CharField(max_length=10,
widget=forms.TextInput(attrs={'id': 'my_field',
'class': 'my_class'}))
self.my_field.label_classes = ('class_a', 'class_b', )
super(MyForm, self).__init__(*args, **kwargs)
Render your template with the form in context, and in the template you can do:
<form>
{% for field in form %}
<label class="{% for class in field.label_classes %}{{ class }} {% endfor %}"
for="{{ field.auto_id }}">{{ field.label }}</label>
{{ field }}
{% endfor %}
<button type="submit">Submit</button>
</form>
Whatever suits your fancy.
Solution 2:
I found this snippet which may be a good answer:
How to add css class and "*" to required fields's labels
here an adaptation to my needs (not tested yet, I'll edit the post once done):
from django.utils.html import escape
def readonly_cssclass_adder(bound_field, label_content, label_attrs):
if 'readonly' in bound_field.field.widget.attrs:
if 'class' in attrs:
label_attrs['class'] += " readOnly"
else:
label_attrs['class'] = "readOnly"
return label_content, label_attrs
def add_required_label_tag(original_function, tweak_foos=None):
if not tweak_foos:
return original_function
def required_label_tag(self, contents=None, attrs=None):
contents = contents or escape(self.label)
if attrs is None:
attrs = {}
for foo in tweak_foos:
contents, attrs = foo(self, contents, attrs)
return original_function(self, contents, attrs)
return required_label_tag
def decorate_bound_field():
from django.forms.forms import BoundField
BoundField.label_tag = add_required_label_tag(BoundField.label_tag,
tweak_foos=[readonly_cssclass_adder])
If you have a better solution which don't tweak all the BoundField class I'm still listening.
edit:
may be linked to django uniform way to handle required field but it seems to don't call readonly_cssclass_adder
. But I found an other and easyer solution, my readOnly widget automatically turned readOnly ctrlHolder
This addition is my favorite response for now:
edit 2: The other way I choose at end was to "override" the uni_form/field.html
template which don't call BoundField.label_tag. So I checked here field state.
<label for="{{ field.auto_id }}"{% if field.field.required %}
class="requiredField{% if field.widget.attrs.readonly %} readOnlyLabel{% endif %}"
{% else %}{% if field.widget.attrs.readonly %}class="readOnlyLabel"{% endif %}{% endif %}>
{{ field.label|safe }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
</label>