Adding custom data-attributes to forms.ChoiceField

Hello, I would like to provide some formatted data to my forms.ChoiceField choices attribute in my Django 5.0 app. I know there were some changes to the “choices” which may be the reason why many options like overriding the “create_option” in my custom Select subclass widget no longer works.

I would like to add my custom data-count attribute to select options but whenever I try to provide
a tuple/dict django converts this to so any “label” data probided to the widget is converted to string I can’t modify the “create_option” in my widget.

Anyone knows about the working (Django >= 5.0) solution? There are many for earlier django versions,
but starting from django 5.0 it does not seem to work.

Can you provide an example of the desired HTML result ?
Do you want the data attributes on the select element or in the option element?

<select>
  <option>----</option>
  <option>Option 1</option>
</select>

Hello,

Sorry for not describing the problem enough. Hope you will find it below.

My desired output would be:

<select>
    <option value="val1" data-count="21">SomeLabel</option>
   <option value="val2" data-count="22"><Some Label2 </option>
</select>

This is a wagtail page (data fetched from Streamfield… there is no problem so it’s not the case here) but I do this using forms.ChoiceField,
and for the clarification I would like to provide (prepared) choices, for example:

raw_choices = [{"value": "val1", "label": "Some Label", "data-count": 21},{"value": "val2", "label": "Some Label2", "data-count": 22, "some-other-attribute": "somevalue"}]

I know I need to proide a simple key-val tupple for forms.ChoiceField(via some function) choices so dediced to subclass Select and add additional extra_data attribute that would be used in a create_option method. Playing with this now, if I get the result I will post here.

Let me know if you know other solution, or maybe I am trying to solve it the wrong way.
This is a common need in my opinion ??

Thanks

From what i can see from digging the source code of django, you maybe can get this working, i haven’t tested it, by:

Creating a CustomSelectWidget

# yourapp/forms.py or yourapp/widgets.py
from django.forms.widgets import Select

class DataCountSelect(Select):
  def __init__(self, attrs=None, choices=()):
    super().__init__(attrs)
    self.choices = [(data["value"], data["label") for data in choices]
    self.data_count_for_choice = {data["value"]: data["data-count"] for data in choices}

  def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
    attrs = attrs or {}
    attrs["data-count"] = self.data_count_for_choice.get(value, 0)
    return super().create_option(name, value, label, selected, index, subindex, attrs)

Using this widget on your form choice field.

Tried your solution, but maybe there’s a problem with a data that the widget in the form is fed with.
Finally ended with a solution below (probably could be written better)

class CustomSelectWithAttrs(Select):
    def __init__(self, *args, **kwargs):
        self.extra_data = kwargs.pop("extra_data", [])
        super().__init__(*args, **kwargs)

    def create_option(
        self, name, value, label, selected, index, subindex=None, attrs=None
    ):
        option = super().create_option(
            name, value, label, selected, index, subindex=subindex, attrs=attrs
        )
        if index < len(self.extra_data):
            for key, val in self.extra_data[index].items():
                option["attrs"][key] = val
        return option
def preprocess_choices(choices):
    choice_field_choices = [
        (choice["value"], choice["label"]) for choice in choices
    ]
    extra_data = [
        {k: v for k, v in choice.items() if k not in ["value", "label"]}
            for choice in choices
        ]

    return choice_field_choices, extra_data

In my form init:

field_choices, field_extra_data = preprocess_choices(raw_choices)
self.fields['some_field'] = forms.ChoiceField(choices=field_choices, widget=CustomSelectWithAttrs(extra_data=field_extra_data)

The logic could probably be moved to the CustomSelectWithAttrs with just providing raw_choices to “choices” widget attribute, but I was getting an error File "/usr/local/lib/python3.12/site-packages/django/forms/widgets.py", line 772, in _choice_has_empty_value value, _ = choice maybe I was doing something wrong.

Thank you for your help and time!
Robert