fromdjango.forms.modelsimportModelChoiceIterator,ModelChoiceFieldclassGroupedModelChoiceField(ModelChoiceField):def__init__(self,group_by_field,group_label=None,*args,**kwargs):""" group_by_field is the name of a field on the model group_label is a function to return a label for each choice group """super(GroupedModelChoiceField,self).__init__(*args,**kwargs)self.group_by_field=group_by_fieldifgroup_labelisNone:self.group_label=lambdagroup:groupelse:self.group_label=group_labeldef_get_choices(self):""" Exactly as per ModelChoiceField except returns new iterator class """ifhasattr(self,'_choices'):returnself._choicesreturnGroupedModelChoiceIterator(self)choices=property(_get_choices,ModelChoiceField._set_choices)classGroupedModelChoiceIterator(ModelChoiceIterator):def__iter__(self):ifself.field.empty_labelisnotNone:yield(u"",self.field.empty_label)ifself.field.cache_choices:ifself.field.choice_cacheisNone:self.field.choice_cache=[(self.field.group_label(group),[self.choice(ch)forchinchoices])forgroup,choicesingroupby(self.queryset.all(),key=lambdarow:getattr(row,self.field.group_by_field))]forchoiceinself.field.choice_cache:yieldchoiceelse:forgroup,choicesingroupby(self.queryset.all(),key=lambdarow:getattr(row,self.field.group_by_field)):yield(self.field.group_label(group),[self.choice(ch)forchinchoices])