Search code examples
djangoselectdjango-formshtml-selectoptgroup

In Django form, custom SelectField and SelectMultipleField


I am using Django everyday now for three month and it is really great. Fast web application development.

I have still one thing that I cannot do exactly how I want to. It is the SelectField and SelectMultiple Field.

I want to be able to put some args to an option of a Select.

I finally success with the optgroup :

class EquipmentField(forms.ModelChoiceField):
    def __init__(self, queryset, **kwargs):
        super(forms.ModelChoiceField, self).__init__(**kwargs)
        self.queryset = queryset
        self.to_field_name=None

        group = None
        list = []
        self.choices = []

        for equipment in queryset:
            if not group:
                group = equipment.type

            if group != equipment.type:
                self.choices.append((group.name, list))
                group = equipment.type
                list = []
            else:
                list.append((equipment.id, equipment.name))

But for another ModelForm, I have to change the background color of every option, using the color property of the model.

Do you know how I can do that ?

Thank you.


Solution

  • render_option has been removed from Django 1.11 onwards. This is what I did to achieve this. A little bit of digging and this seems straightforward and neat. Works with Django 2.0+

    class CustomSelect(forms.Select):
        def __init__(self, attrs=None, choices=()):
            self.custom_attrs = {}
            super().__init__(attrs, choices)
    
        def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
            index = str(index) if subindex is None else "%s_%s" % (index, subindex)
            if attrs is None:
                attrs = {}
            option_attrs = self.build_attrs(self.attrs, attrs) if self.option_inherits_attrs else {}
            if selected:
                option_attrs.update(self.checked_attribute)
            if 'id' in option_attrs:
                option_attrs['id'] = self.id_for_label(option_attrs['id'], index)
    
            # setting the attributes here for the option
            if len(self.custom_attrs) > 0:
                if value in self.custom_attrs:
                    custom_attr = self.custom_attrs[value]
                    for k, v in custom_attr.items():
                        option_attrs.update({k: v})
    
            return {
                'name': name,
                'value': value,
                'label': label,
                'selected': selected,
                'index': index,
                'attrs': option_attrs,
                'type': self.input_type,
                'template_name': self.option_template_name,
            }
    
    
    class MyModelChoiceField(ModelChoiceField):
    
        # custom method to label the option field
        def label_from_instance(self, obj):
            # since the object is accessible here you can set the extra attributes
            if hasattr(obj, 'type'):
                self.widget.custom_attrs.update({obj.pk: {'type': obj.type}})
            return obj.get_display_name()
    

    The form:

    class BookingForm(forms.ModelForm):
    
        customer = MyModelChoiceField(required=True,
                                      queryset=Customer.objects.filter(is_active=True).order_by('name'),
                                      widget=CustomSelect(attrs={'class': 'chosen-select'}))
    

    The output which I needed is as:

      <select name="customer" class="chosen-select" required="" id="id_customer">
          <option value="" selected="">---------</option>
          <option value="242" type="CNT">AEC Transcolutions Private Limited</option>
          <option value="243" type="CNT">BBC FREIGHT CARRIER</option>
          <option value="244" type="CNT">Blue Dart Express Limited</option>