Как сгруппировать варианты в виджете Django Select?

Можно ли создать именованные группы выбора в виджете Django select (раскрывающийся список), когда этот виджет находится в форме, которая автоматически генерируется из модели данных? Могу ли я создать виджет на левом изображении ниже?

Two widgets with one grouped

Мой первый эксперимент по созданию формы с именованными группами был проведен вручную , вот так:

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 как указатель на данные, а не фактический data).

Код, который я использовал для модели данных:

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 ' ответ :

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 для вариантов выбора , вместо того, чтобы переопределять весь класс . Что очень приятно.

20
задан Community 23 May 2017 в 12:08
поделиться