How to add attributes to option tags in django ? How to add attributes to option tags in django ? django django

How to add attributes to option tags in django ?


First of all, don't modify fields in __init__, if you want to override widgets use Meta inner class, if you want to override form fields, declare them like in a normal (non-model) form.

If the Select widget does not do what you want, then simply make your own. Original widget uses render_option method to get HTML representation for a single option — make a subclass, override it, and add whatever you want.

class MySelect(forms.Select):    def render_option(self, selected_choices, option_value, option_label):        # look at the original for something to start with        return u'<option whatever>...</option>'class LocModelForm(forms.ModelForm):    icons = forms.ModelChoiceField(        queryset = Photo.objects.filter(galleries__title_slug = "markers"),        widget = MySelect(attrs = {'id': 'mydds'})    )    class Meta:        # ...        # note that if you override the entire field, you don't have to override        # the widget here    class Media:        # ...


I had a similar problem, where I needed to add a custom attribute to each option dynamically. But in Django 2.0, the html rendering was moved into the Widget base class, so modifying render_option no longer works. Here is the solution that worked for me:

from django import formsclass CustomSelect(forms.Select):    def __init__(self, *args, **kwargs):        self.src = kwargs.pop('src', {})        super().__init__(*args, **kwargs)    def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):        options = super(CustomSelect, self).create_option(name, value, label, selected, index, subindex=None, attrs=None)        for k, v in self.src.items():            options['attrs'][k] = v[options['value']]        return optionsclass CustomForm(forms.Form):    def __init__(self, *args, **kwargs):        src = kwargs.pop('src', {})        choices = kwargs.pop('choices', ())        super().__init__(*args, **kwargs)        if choices:            self.fields['custom_field'].widget = CustomSelect(attrs={'class': 'some-class'}, src=src, choices=choices)    custom_field = forms.CharField(max_length=100)

Then in views, render a context with {'form': CustomForm(choices=choices, src=src)} where src is a dictionary like this: {'attr-name': {'option_value': 'attr_value'}}.


Here's a class I made that inherits from forms.Select (thanks to Cat Plus Plus for getting me started with this). On initialization, provide the option_title_field parameter indicating which field to use for the <option> title attribute.

from django import formsfrom django.utils.html import escapeclass SelectWithTitle(forms.Select):    def __init__(self, attrs=None, choices=(), option_title_field=''):        self.option_title_field = option_title_field        super(SelectWithTitle, self).__init__(attrs, choices)    def render_option(self, selected_choices, option_value, option_label, option_title=''):        print option_title        option_value = forms.util.force_unicode(option_value)        if option_value in selected_choices:            selected_html = u' selected="selected"'            if not self.allow_multiple_selected:                # Only allow for a single selection.                selected_choices.remove(option_value)        else:            selected_html = ''        return u'<option title="%s" value="%s"%s>%s</option>' % (            escape(option_title), escape(option_value), selected_html,            forms.util.conditional_escape(forms.util.force_unicode(option_label)))    def render_options(self, choices, selected_choices):            # Normalize to strings.            selected_choices = set(forms.util.force_unicode(v) for v in selected_choices)            choices = [(c[0], c[1], '') for c in choices]            more_choices = [(c[0], c[1]) for c in self.choices]            try:                option_title_list = [val_list[0] for val_list in self.choices.queryset.values_list(self.option_title_field)]                if len(more_choices) > len(option_title_list):                    option_title_list = [''] + option_title_list # pad for empty label field                more_choices = [(c[0], c[1], option_title_list[more_choices.index(c)]) for c in more_choices]            except:                more_choices = [(c[0], c[1], '') for c in more_choices] # couldn't get title values            output = []            for option_value, option_label, option_title in chain(more_choices, choices):                if isinstance(option_label, (list, tuple)):                    output.append(u'<optgroup label="%s">' % escape(forms.util.force_unicode(option_value)))                    for option in option_label:                        output.append(self.render_option(selected_choices, *option, **dict(option_title=option_title)))                    output.append(u'</optgroup>')                else: # option_label is just a string                    output.append(self.render_option(selected_choices, option_value, option_label, option_title))            return u'\n'.join(output)class LocModelForm(forms.ModelForm):    icons = forms.ModelChoiceField(        queryset = Photo.objects.filter(galleries__title_slug = "markers"),        widget = SelectWithTitle(option_title_field='FIELD_NAME_HERE')    )