Формы Django, ModelChoiceField с использованием виджета RadioSelect, сгруппированные по FK

Задача, вывести радиовыбор во вложенных <ul></ul>, сгруппированных по задаче fk.

ie.

class Category(models.Model):
    # ...

class Task(models.Model):
    # ...
    category = models.ForeignKey(Category)
    # ...

формы.py

class ActivityForm(forms.ModelForm):
    # ...
    task = forms.ModelChoiceField(
        queryset    = Task.objects.all(),
        widget      = RadioSelectGroupedByFK
    )

виджеты.py

class RadioFieldRendererGroupedByFK(RadioFieldRenderer):
    """
    An object used by RadioSelect to enable customization of radio widgets.
    """
    #def __init__(self, attrs=None):
        # Need a radio select for each?? Just an Idea.
        #widgets = (RadioSelect(attrs=attrs), RadioSelect(attrs=attrs))
        #super(RadioFieldRendererGroupedByFK, self).__init__(widgets, attrs)

    def render(self):
        """Outputs nested <ul> for this set of radio fields."""
        return mark_safe(
            #### Somehow the crux of the work happens here? but how to get the
            #### right context??
            u'<ul>\n%s\n</ul>' % u'\n'.join(
                [u'<li>%s</li>' % force_unicode(w) for w in self]
            )
        )

class RadioSelectGroupedByFK(forms.RadioSelect):
    renderer = RadioFieldRendererGroupedByFK

Лучшее спасибо!!


person Antonius Common    schedule 18.03.2009    source источник


Ответы (1)


Для этого идеально подходит itertools.groupby(). Я предполагаю, что Task и Category имеют атрибут name, по которому вы хотите их отсортировать. Я не знаком с API Django, поэтому вы, вероятно, захотите перенести сортировку в запрос базы данных, и вам нужно будет посмотреть на атрибуты виджета, чтобы выяснить, как получить доступ к объектам задачи, но вот основная формула:

from itertools import groupby

# sort first since groupby only groups adjacent items
tasks.sort(key=lambda task: (task.category.name, task.name))

for category, category_tasks in groupby(tasks, key=lambda task: task.category):
  print '%s:' % category.name
  for task in category_tasks:
    print '* %s' % task.name

Это должно напечатать список, например:

Breakfast foods:
* eggs
* spam
Dinner foods:
* spam
* spam
* spam

ХТН

person Matt Good    schedule 25.03.2009