I want to add an attribute to model choice field on a form. The intent to do some javascript like hide or show based on a filter. For example, suppose there’s a two state status on each choice, like active or inactive. These are checkboxes, which onChange
should make choices with appear or disappear based on whether the checkbox is checked. The attribute to add to each option is generated on each page load, as they are transient. We’re not using APIs for this app just yet.
I’ve been playing around with different solutions I’ve found on-line. They all hinge on extending widget.Select
to add the custom field to the attrs. After trying several variations, I found the right search phrase that led me to a solution in the django doc: https://docs.djangoproject.com/en/4.2/ref/forms/fields/#modelmultiplechoicefield. Unfortunately, what’s in the doc isn’t what I’m seeing.
from django import forms
class ToppingSelect(forms.Select):
def create_option(
self, name, value, label, selected, index, subindex=None, attrs=None
):
option = super().create_option(
name, value, label, selected, index, subindex, attrs
)
if value:
option["attrs"]["data-price"] = value.instance.price
return option
class PizzaForm(forms.ModelForm):
class Meta:
model = Pizza
fields = ["topping"]
widgets = {"topping": ToppingSelect}
So I adapted the solution:
class CustomSelectWithAttrs(forms.Select):
def create_option(
self, name, value, label, selected, index, subindex=None, attrs=None
):
option = super().create_option(
name, value, label, selected, index, subindex, attrs
)
print(f'option = {option}')
print(f'\tattrs = {attrs}')
print(f'\tvalue = {value} instance={value.instance}')
if value:
print(f'\tstatus = {value.instance.status}')
option["attrs"]['current-status'] = value.instance.status.state
print(f'all set now: option = {option}')
return option
Here’s the form
class MatchFilterForm(ModelForm):
class Meta:
model = Match
fields = {'tutor', 'student', 'match_status', 'date_started', 'comments'}
exclude = {'lvm_affiliate', 'date_dissolved', }
labels = {'tutor': 'Tutor pool',
'student': 'Student pool',
'match_status': "Status",
'date_started': "Start date",
'comments': 'Comments'}
widgets = {
'date_started': DateInput(
format='%Y-%m-%d',
attrs={
'class': 'form-control',
'placeholder': 'Select a date',
'type': 'date'
}
),
'student': CustomSelectWithAttrs,
}
This renders to
which is great. The html is not so great - the attribute isn’t there:
I’m confused because it looks like the widget’s
create_option
is working:The option initializes in the line that starts option = and
attrs = {}
The custom attribute are in in attrs in the line that starts all set now.