Login

Grouped Model Choice Field

Author:
JulieGoldberg
Posted:
April 8, 2015
Language:
Python
Version:
Not specified
Tags:
ModelChoiceField OptGroup Forms
Score:
1 (after 1 ratings)

This class lets me have a model choice field that includes optgroups . Django has built-in support for optgroups if you explicitly set all the choices in a ChoiceField, but that doesn't help with ModelChoiceFields. Optgroups let you have nice-looking subscategories in huge dropdowns. They're even more useful if you're using something like selectize.js, because you have a ton of options.

If you inherit from GroupedModelChoiceField and override the optgroup_from_instance function, as in SampleChoiceField, you'll get a dropdown with your models with the expected optgroup tags in the html. Be sure to have your queryset first order by whatever you're displaying in optgroup.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
from django import forms

class GroupedModelChoiceField(forms.ModelChoiceField):

    def optgroup_from_instance(self, obj):
        return ""

    def __choice_from_instance__(self, obj):
        return (obj.id, self.label_from_instance(obj))

    def _get_choices(self):
        if not self.queryset:
            return []

        all_choices = []
        if self.empty_label:
            current_optgroup = ""
            current_optgroup_choices = [("", self.empty_label)]
        else:
            current_optgroup = self.optgroup_from_instance(self.queryset[0])
            current_optgroup_choices = []

        for item in self.queryset:
            optgroup_from_instance = self.optgroup_from_instance(item)
            if current_optgroup != optgroup_from_instance:
                all_choices.append((current_optgroup, current_optgroup_choices))
                current_optgroup_choices = []
                current_optgroup = optgroup_from_instance
            current_optgroup_choices.append(self.__choice_from_instance__(item))

        all_choices.append((current_optgroup, current_optgroup_choices))

        return all_choices

    choices = property(_get_choices, forms.ChoiceField._set_choices)

# Example subclass
class ExampleChoiceField(GroupedModelChoiceField):

    def optgroup_from_instance(self, obj):
	return obj.group.name

More like this

Comments

christippett (on April 18, 2015):

This worked great. Thanks for sharing.

#

gregb (on September 7, 2016):

Great, thanks. One small improvement - replace lines 15-21 with this:

    all_choices = []
    if self.empty_label:
        all_choices.append(("", self.empty_label))

    current_optgroup = self.optgroup_from_instance(self.queryset[0])
    current_optgroup_choices = []

There's no need to have the empty choice in its own optgroup.

#

Please login first before commenting.