Przy­pu­śćmy, że nasza apli­ka­cja pozwala kupić koszulkę. Dostępne kolory zależą od roz­miaru i trzeba pozwo­lić je jakoś wybrać. Dwa selecty? Będzie się dało wybrać nie­ist­nie­jącą kom­bi­na­cję. Łączony select? Nie­czy­tel­nie, chyba że…

Poniż­sze roz­wią­za­nie używa klasy GroupedSelect pocho­dzą­cej z Django Snip­pets.

from django.forms import ModelChoiceField, ValidationError
from django.forms.util import smart_unicode
from django.utils.itercompat import groupby
from operator import attrgetter

class GroupedModelChoiceIterator(object):
    def __init__(self, field, grouper):
		self.field = field
		self.queryset = field.queryset
		self.grouper = grouper

    def __iter__(self):
		if self.field.empty_label is not None:
			yield (None, ((u'', self.field.empty_label), ))
		for grouper, items in groupby(self.queryset.all(), attrgetter(self.grouper)):
			yield (unicode(grouper), tuple((o.pk, unicode(o)) for o in items))

class GroupedModelChoiceField(ModelChoiceField):
	def __init__(self, field, widget = GroupedSelect, *args, **kwargs):
		self._field = field
		super(GroupedModelChoiceField, self).__init__(widget = widget, *args, **kwargs)

	def _get_choices(self):
		return GroupedModelChoiceIterator(self, self._field)

	choices = property(_get_choices, None)

	def clean(self, value):
		"""
		Validates that the input is in self.choices.
		"""
		if value in (None, ''):
			value = u''
		value = smart_unicode(value)
		valid_values = []
		for group_label, group in self.choices:
			valid_values += [str(k) for k, v in group]
		if value not in valid_values:
			raise ValidationError(u'Select a valid choice. That choice is not one of the available choices.')
		return super(GroupedModelChoiceField, self).clean(value)

Przy­kład (zakła­da­jąc, że color jest refe­ren­cją na obiekt, który posiada pole size):

class SizeAndColorForm(forms.ModelForm):
	class Meta:
		model = Product
		fields = ['color']
	color = GroupedModelChoiceField(field = 'size', label = u'size/color')