Можно ли создать именованные группы выбора в виджетах выбора (выпадающего) Django, когда этот виджет находится в форме, которая автоматически генерируется из модели данных? Могу ли я создать виджет на левом рисунке ниже?
Мой первый эксперимент по созданию формы с именованными группами был выполнен вручную, например:
class GroupMenuOrderForm(forms.Form):
food_list = [(1, 'burger'), (2, 'pizza'), (3, 'taco'),]
drink_list = [(4, 'coke'), (5, 'pepsi'), (6, 'root beer'),]
item_list = ( ('food', tuple(food_list)), ('drinks', tuple(drink_list)),)
itemsField = forms.ChoiceField(choices = tuple(item_list))
def GroupMenuOrder(request):
theForm = GroupMenuOrderForm()
return render_to_response(menu_template, {'form': theForm,})
# generates the widget in left-side picture
И он работал красиво, создавая виджет выпадающего слева, с именованными группами.
Затем я создал модель данных, которая имела в основном одну и ту же структуру, и использовала способность Django автоматически генерировать формы из моделей. Он работал - в том смысле, что он показал все варианты. Но варианты не были в названных группах, и до сих пор я не понял, как это сделать - если это даже возможно.
Я нашел несколько вопросов, в которых был ответ: "создайте конструктор формы и выполните любую специальную обработку". Но похоже, что формы .ChoiceField требует кортеж для именованных групп, и я не уверен, как преобразовать кортеж в QuerySet (что, вероятно, невозможно в любом случае, если я правильно понимаю QuerySets как указатель на данные, а не фактические данные).
Код, который я использовал для модели данных:
class ItemDesc(models.Model):
''' one of "food", "drink", where ID of "food" = 1, "drink" = 2 '''
desc = models.CharField(max_length=10, unique=True)
def __unicode__(self):
return self.desc
class MenuItem(models.Model):
''' one of ("burger", 1), ("pizza", 1), ("taco", 1),
("coke", 2), ("pepsi", 2), ("root beer", 2) '''
name = models.CharField(max_length=50, unique=True)
itemDesc = models.ForeignKey(ItemDesc)
def __unicode__(self):
return self.name
class PatronOrder(models.Model):
itemWanted = models.ForeignKey(MenuItem)
class ListMenuOrderForm(forms.ModelForm):
class Meta:
model = PatronOrder
def ListMenuOrder(request):
theForm = ListMenuOrderForm()
return render_to_response(menu_template, {'form': theForm,})
# generates the widget in right-side picture
Я изменил бы модель данных, если понадобится, но это казалось простой структурой. Может быть, слишком много ForeignKeys? Свернуть данные и принять денормализацию?:) Или есть способ конвертировать кортеж в QuerySet или что-то приемлемое для ModelChoiceField?
Обновить: окончательный код на основе meshantz answer
class FooIterator(forms.models.ModelChoiceIterator):
def __init__(self, *args, **kwargs):
super(forms.models.ModelChoiceIterator, self).__init__(*args, **kwargs)
def __iter__(self):
yield ('food', [(1L, u'burger'), (2L, u'pizza')])
yield ('drinks', [(3L, u'coke'), (4L, u'pepsi')])
class ListMenuOrderForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ListMenuOrderForm, self).__init__(*args, **kwargs)
self.fields['itemWanted'].choices = FooIterator()
class Meta:
model = PatronOrder
(Конечно, фактический код, я кое-что вытащил данные элемента из базы данных.)
Самое большое изменение, которое он связал с djangosnippet, похоже, заключается в том, что Django включил часть кода, позволяя прямо назначать Iterator для выбора, вместо того, чтобы переопределять весь класс. Что очень приятно.